use ifc_model::{Model, Value};
use ifc_schema::Schema;
use crate::report::{Finding, Path, Report};
pub fn dangling_references(model: &Model, report: &mut Report) {
let mut ids: Vec<_> = model.iter().map(|(id, _)| id).collect();
ids.sort_unstable();
for id in ids {
let Some(entity) = model.get(id) else {
continue;
};
for (index, value) in entity.attributes.iter().enumerate() {
let mut missing = Vec::new();
value.for_each_ref(&mut |target| {
if model.get(target).is_none() {
missing.push(target);
}
});
missing.sort_unstable();
missing.dedup();
for target in missing {
report.push(Finding::error(
"structure.reference.dangling",
Path::Attribute {
entity: id,
index,
name: None,
},
format!("references {target}, which the file does not contain"),
));
}
}
}
}
pub fn wrong_kind_references(model: &Model, schema: &Schema, report: &mut Report) {
let mut ids: Vec<_> = model.iter().map(|(id, _)| id).collect();
ids.sort_unstable();
for id in ids {
let Some(entity) = model.get(id) else {
continue;
};
let declared = schema.attributes(&entity.type_name);
for (index, value) in entity.attributes.iter().enumerate() {
let Some(attribute) = declared.get(index) else {
continue;
};
if schema.entity(&attribute.type_name).is_none() {
continue;
}
let Value::Ref(target) = value.unwrap_typed() else {
continue;
};
let Some(target_entity) = model.get(*target) else {
continue; };
if !schema.is_a(&target_entity.type_name, &attribute.type_name) {
report.push(Finding::error(
"structure.reference.wrong_type",
Path::Attribute {
entity: id,
index,
name: Some(attribute.name.clone()),
},
format!(
"declared {} but {target} is {}",
attribute.type_name, target_entity.type_name
),
));
}
}
}
}