use std::fmt;
use crate::codec::{Files, LABELS, LINT, MANIFEST, ReadError, Site, Sites, SpecPath, read_sited};
use crate::conflict::ConflictOwner;
use crate::lint::{Finding, LintConfig, lint};
use crate::validate::{ValidationError, validate};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Placed {
pub error: ValidationError,
pub sites: Vec<Site>,
}
impl fmt::Display for Placed {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
for site in &self.sites {
write!(formatter, "{site}: ")?;
}
self.error.fmt(formatter)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Checked {
Unread(Vec<ReadError>),
Read {
invalid: Vec<Placed>,
config: Vec<ReadError>,
findings: Vec<Finding>,
},
}
impl Checked {
#[must_use]
pub fn has_errors(&self) -> bool {
match self {
Self::Unread(_) => true,
Self::Read {
invalid, config, ..
} => !invalid.is_empty() || !config.is_empty(),
}
}
#[must_use]
pub fn findings(&self) -> &[Finding] {
match self {
Self::Unread(_) => &[],
Self::Read { findings, .. } => findings,
}
}
}
#[must_use]
pub fn check(files: &Files) -> Checked {
let (document, sites) = match read_sited(files) {
Ok(read) => read,
Err(errors) => return Checked::Unread(errors),
};
let invalid = placed(validate(&document), &sites);
let config = LintConfig::read(files.get(&SpecPath::new(LINT)).map(Vec::as_slice));
let (config, findings) = match config {
Ok(config) => (Vec::new(), lint(&document, &sites, &config)),
Err(errors) => (errors, Vec::new()),
};
Checked::Read {
invalid,
config,
findings,
}
}
pub(crate) fn placed(errors: Vec<ValidationError>, sites: &Sites) -> Vec<Placed> {
errors
.into_iter()
.map(|error| place(error, sites))
.collect()
}
fn place(error: ValidationError, sites: &Sites) -> Placed {
let file = match &error {
ValidationError::Conflict {
owner: ConflictOwner::Manifest,
..
} => Some(MANIFEST),
ValidationError::Conflict {
owner: ConflictOwner::Labels,
..
} => Some(LABELS),
_ => None,
};
let sites = file
.map(|path| Site {
path: SpecPath::new(path),
location: None,
})
.into_iter()
.chain(
error
.keys()
.into_iter()
.filter_map(|key| sites.get(key).cloned()),
)
.collect();
Placed { error, sites }
}