#![allow(clippy::wildcard_imports)]
use super::*;
pub(super) fn check_version(ir: &CadIr, findings: &mut Vec<Finding>) {
if ir.ir_version() != IR_VERSION {
findings.push(Finding {
check: Check::Version,
severity: Severity::Error,
message: format!(
"unsupported ir_version {:?}; expected {IR_VERSION}",
ir.ir_version()
),
entity: None,
});
}
}
fn push_identity(seen: &mut HashSet<String>, findings: &mut Vec<Finding>, id: &str) {
if !crate::ids::is_valid_identity(id) {
findings.push(Finding {
check: Check::Identity,
severity: Severity::Error,
message: "entity id does not match `<format>:<scope>:<kind>#<key>`".into(),
entity: Some(id.to_owned()),
});
}
if !seen.insert(id.to_owned()) {
findings.push(Finding {
check: Check::Identity,
severity: Severity::Error,
message: "entity id is not globally unique".into(),
entity: Some(id.to_owned()),
});
}
}
pub(super) fn check_order<'a>(
arena: &str,
ids: impl IntoIterator<Item = &'a str>,
findings: &mut Vec<Finding>,
) {
let mut previous: Option<&str> = None;
for id in ids {
if previous.is_some_and(|value| value >= id) {
findings.push(Finding {
check: Check::ArenaOrder,
severity: Severity::Error,
message: format!("arena `{arena}` is not strictly sorted by id"),
entity: Some(id.to_owned()),
});
return;
}
previous = Some(id);
}
}
macro_rules! define_model_identity_checks {
($( $field:ident: $element:ty, $doc:literal, [$($attribute:meta),*]; )*) => {
fn check_model_identity_and_order(
ir: &CadIr,
seen: &mut HashSet<String>,
findings: &mut Vec<Finding>,
) {
$(
check_order(
stringify!($field),
ir.model.$field.iter().map(crate::schema::EntitySchema::identity),
findings,
);
for entity in &ir.model.$field {
push_identity(seen, findings, crate::schema::EntitySchema::identity(entity));
}
)*
}
};
}
crate::document::arena_registry!(define_model_identity_checks);
pub(super) fn check_identity_and_order(ir: &CadIr, findings: &mut Vec<Finding>) {
let mut seen = HashSet::new();
check_model_identity_and_order(ir, &mut seen, findings);
let native_ids = collect_native_ids(ir);
for (_, id) in &native_ids {
push_identity(&mut seen, findings, id);
}
let mut by_arena: BTreeMap<&str, Vec<&str>> = BTreeMap::new();
for (arena, id) in &native_ids {
by_arena.entry(arena).or_default().push(id);
}
for (arena, ids) in by_arena {
check_order(arena, ids, findings);
}
}
pub(super) fn collect_native_ids(ir: &CadIr) -> Vec<(String, &str)> {
ir.native
.0
.iter()
.flat_map(|(format, namespace)| {
namespace.arenas.iter().flat_map(move |(arena, records)| {
records
.iter()
.map(move |record| (format!("native.{format}.{arena}"), record.id()))
})
})
.collect()
}
#[cfg(test)]
mod tests;