blob: 754748fcabd801534f88f1ee46a57d05cd3acda4 [file] [log] [blame]
mod package_spec;
use crate::diag;
pub use package_spec::{PackageSpec, PackageSpecOrExtended};
pub use toml_span::span::{Span, Spanned};
pub struct ValidationContext<'ctx> {
pub cfg_id: diag::FileId,
pub files: &'ctx mut diag::Files,
pub diagnostics: &'ctx mut Vec<diag::Diagnostic>,
}
impl<'ctx> ValidationContext<'ctx> {
#[inline]
pub fn push(&mut self, diag: diag::Diagnostic) {
self.diagnostics.push(diag);
}
/// Sorts a vec and prints a warning about duplicate items before removing them
pub fn dedup<T>(&mut self, v: &mut Vec<Spanned<T>>)
where
T: Ord,
{
if v.len() <= 1 {
return;
}
v.sort();
for window in v.windows(2) {
if window[0] != window[1] {
continue;
}
self.push(
diag::Diagnostic::warning()
.with_message("duplicate items detected")
.with_labels(vec![
diag::Label::secondary(self.cfg_id, window[0].span),
diag::Label::secondary(self.cfg_id, window[1].span),
]),
);
}
v.dedup();
}
}
pub trait UnvalidatedConfig {
type ValidCfg;
fn validate(self, ctx: ValidationContext<'_>) -> Self::ValidCfg;
}
#[derive(Clone)]
#[cfg_attr(test, derive(Debug, PartialEq, Eq, serde::Serialize))]
pub struct Reason(pub Spanned<String>);
impl From<Spanned<String>> for Reason {
fn from(s: Spanned<String>) -> Self {
Self(s)
}
}
impl<'de> toml_span::Deserialize<'de> for Reason {
fn deserialize(
value: &mut toml_span::value::Value<'de>,
) -> Result<Self, toml_span::DeserError> {
let mut th = toml_span::de_helpers::TableHelper::new(value)?;
let r = th.required("reason")?;
th.finalize(Some(value))?;
Ok(Self(r))
}
}
/// Deserialize a field from the table if it exists, but append the key's span
/// so it can be marked as deprecated
pub fn deprecated<'de, T>(
th: &mut toml_span::de_helpers::TableHelper<'de>,
field: &'static str,
spans: &mut Vec<Span>,
) -> Option<T>
where
T: toml_span::Deserialize<'de>,
{
let (k, mut v) = th.take(field)?;
spans.push(k.span);
match T::deserialize(&mut v) {
Ok(v) => Some(v),
Err(mut err) => {
th.errors.append(&mut err.errors);
None
}
}
}