ifc_validate/type_check/
entity.rs1use ifc_model::Model;
4use ifc_schema::Schema;
5
6use super::defined::{check, Mismatch};
7use crate::report::{Finding, Path, Report};
8
9pub fn unknown_entity_types(model: &Model, schema: &Schema, report: &mut Report) {
15 let mut ids: Vec<_> = model.iter().map(|(id, _)| id).collect();
16 ids.sort_unstable();
17 for id in ids {
18 let Some(entity) = model.get(id) else {
19 continue;
20 };
21 if schema.entity(&entity.type_name).is_none() {
22 report.push(Finding::warning(
23 "type.entity.unknown",
24 Path::Entity(id),
25 format!(
26 "{} is not declared by schema {}",
27 entity.type_name,
28 schema.name()
29 ),
30 ));
31 }
32 }
33}
34
35pub fn abstract_instances(model: &Model, schema: &Schema, report: &mut Report) {
40 let mut ids: Vec<_> = model.iter().map(|(id, _)| id).collect();
41 ids.sort_unstable();
42 for id in ids {
43 let Some(entity) = model.get(id) else {
44 continue;
45 };
46 let Some(definition) = schema.entity(&entity.type_name) else {
47 continue;
48 };
49 if definition.abstract_ {
50 report.push(Finding::error(
51 "type.entity.abstract",
52 Path::Entity(id),
53 format!("{} is abstract and cannot be instantiated", definition.name),
54 ));
55 }
56 }
57}
58
59pub fn attribute_types(model: &Model, schema: &Schema, report: &mut Report) {
61 let mut ids: Vec<_> = model.iter().map(|(id, _)| id).collect();
62 ids.sort_unstable();
63 for id in ids {
64 let Some(entity) = model.get(id) else {
65 continue;
66 };
67 let declared = schema.attributes(&entity.type_name);
68 for (index, value) in entity.attributes.iter().enumerate() {
69 let Some(attribute) = declared.get(index) else {
70 continue;
71 };
72 let Some(mismatch) = check(schema, &attribute.type_name, value) else {
73 continue;
74 };
75 let path = Path::Attribute {
76 entity: id,
77 index,
78 name: Some(attribute.name.clone()),
79 };
80 let finding = match mismatch {
81 Mismatch::Primitive { expected, actual } => Finding::error(
82 "type.scalar.mismatch",
83 path,
84 format!(
85 "{} is {expected}, the file wrote {actual}",
86 attribute.type_name
87 ),
88 ),
89 Mismatch::FixedWidth { expected, actual } => Finding::error(
90 "type.scalar.fixed_width",
91 path,
92 format!(
93 "{} is STRING({expected}) FIXED, the file wrote {actual} characters",
94 attribute.type_name
95 ),
96 ),
97 Mismatch::EnumMember { member, declared } => Finding::error(
98 "type.enumeration.member",
99 path,
100 format!(
101 "{member} is not a member of {} ({})",
102 attribute.type_name,
103 if declared.is_empty() {
104 "no members declared".to_string()
105 } else {
106 declared.join(", ")
107 }
108 ),
109 ),
110 Mismatch::SelectMember { written, select } => Finding::error(
111 "type.select.member",
112 path,
113 format!("{written} is not a member of {select}"),
114 ),
115 };
116 report.push(finding);
117 }
118 }
119}