ifc_alignment/error.rs
1//! Structured alignment interpretation failures.
2
3use ifc_model::EntityId;
4
5/// Why an IFC alignment entity could not be read or lowered.
6#[non_exhaustive]
7#[derive(Debug, Clone, PartialEq)]
8pub enum AlignmentError {
9 /// A referenced entity id is not present in the model.
10 MissingEntity {
11 /// The id that resolved to nothing.
12 entity: EntityId,
13 },
14 /// An entity was not the IFC type the referencing slot requires.
15 WrongType {
16 /// The entity that was read.
17 entity: EntityId,
18 /// The IFC type the slot requires.
19 expected: &'static str,
20 /// The IFC type actually declared.
21 actual: String,
22 },
23 /// A mandatory attribute was absent, so the value cannot be inferred.
24 MissingAttribute {
25 /// The entity that was read.
26 entity: EntityId,
27 /// Zero-based slot index of the absent attribute.
28 index: usize,
29 /// Schema name of the attribute.
30 name: &'static str,
31 },
32 /// An attribute was present but held the wrong kind of value.
33 InvalidAttribute {
34 /// The entity that was read.
35 entity: EntityId,
36 /// Zero-based slot index of the offending attribute.
37 index: usize,
38 /// Schema name of the attribute.
39 name: &'static str,
40 },
41 /// The project's unit assignment does not resolve to usable lengths or angles.
42 InvalidUnits {
43 /// What expectation failed.
44 detail: &'static str,
45 },
46 /// A horizontal, vertical, or cant segment carries internally inconsistent data.
47 InvalidSegment {
48 /// The segment entity.
49 entity: EntityId,
50 /// What expectation failed.
51 detail: &'static str,
52 },
53 /// A segment type or configuration this crate deliberately does not lower.
54 ///
55 /// Covers segment kinds that are legal IFC but require data this crate does
56 /// not yet combine (e.g. `VIENNESEBEND` needs the cant swing carried by a
57 /// separate `IfcAlignmentCant` layout) or that have no closed-form neutral
58 /// curve here (e.g. `CUBIC`).
59 Unsupported {
60 /// The unsupported entity.
61 entity: EntityId,
62 /// The IFC type or `PredefinedType` name actually declared.
63 type_name: String,
64 /// Why it is refused rather than approximated.
65 detail: &'static str,
66 },
67 /// The assembled neutral alignment graph violates a structural invariant.
68 Graph {
69 /// What expectation failed.
70 detail: String,
71 },
72 /// The model header declares no `FILE_SCHEMA` token.
73 MissingSchema,
74 /// More than one `FILE_SCHEMA` token was declared.
75 AmbiguousSchema {
76 /// Every schema token found in the header.
77 tokens: Vec<String>,
78 },
79 /// The declared schema is not one this crate can interpret.
80 ///
81 /// Alignment entities (`IfcAlignment*`) were introduced in IFC4X3; IFC2X3
82 /// and IFC4 ADD2 TC1 do not declare them at all, so there is no version
83 /// dispatch here the way `ifc-resource`/`ifc-structural` have one --
84 /// exactly one profile is authoritative and anything else is refused.
85 UnsupportedSchema {
86 /// The rejected schema token.
87 token: String,
88 },
89 /// A relationship or nesting structure violates a stated invariant.
90 SemanticViolation {
91 /// The offending entity, when one could be identified.
92 entity: Option<EntityId>,
93 /// The invariant that was violated.
94 rule: &'static str,
95 },
96 /// An authored value was rejected before anything was staged (ADR 0011).
97 ///
98 /// Distinct from [`Self::InvalidSegment`], which describes a segment that
99 /// already exists in a file. Here there is no entity yet and therefore no
100 /// id to report: the caller passed a value that could not be written.
101 InvalidAuthoredValue {
102 /// The IFC type being authored.
103 type_name: &'static str,
104 /// The attribute whose value was rejected.
105 attribute: &'static str,
106 /// Why it cannot be written.
107 detail: String,
108 },
109 /// A dangling reference: the target id is not present in the model.
110 DanglingReference {
111 /// The entity holding the dangling reference.
112 entity: EntityId,
113 /// The attribute that names the missing target.
114 attribute: &'static str,
115 /// The id that resolved to nothing.
116 target: EntityId,
117 },
118 /// Graph traversal exceeded an explicit depth or node-count bound.
119 BudgetExceeded {
120 /// The configured maximum traversal depth.
121 max_depth: usize,
122 /// The configured maximum node count.
123 max_nodes: usize,
124 },
125}
126
127/// Result of an alignment read, carrying [`AlignmentError`] on failure.
128pub type AlignmentResult<T> = Result<T, AlignmentError>;
129
130impl std::fmt::Display for AlignmentError {
131 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132 match self {
133 Self::MissingEntity { entity } => write!(f, "missing alignment entity {entity}"),
134 Self::WrongType {
135 entity,
136 expected,
137 actual,
138 } => write!(f, "{entity} is {actual}; expected {expected}"),
139 Self::MissingAttribute {
140 entity,
141 index,
142 name,
143 } => write!(f, "{entity} misses {name} at slot {index}"),
144 Self::InvalidAttribute {
145 entity,
146 index,
147 name,
148 } => write!(f, "{entity} has invalid {name} at slot {index}"),
149 Self::InvalidUnits { detail } => write!(f, "invalid alignment units: {detail}"),
150 Self::InvalidSegment { entity, detail } => {
151 write!(f, "invalid alignment segment {entity}: {detail}")
152 }
153 Self::Unsupported {
154 entity,
155 type_name,
156 detail,
157 } => write!(f, "unsupported {type_name} at {entity}: {detail}"),
158 Self::Graph { detail } => write!(f, "invalid neutral alignment graph: {detail}"),
159 Self::MissingSchema => write!(f, "no FILE_SCHEMA token was declared"),
160 Self::AmbiguousSchema { tokens } => {
161 write!(f, "ambiguous FILE_SCHEMA tokens: {tokens:?}")
162 }
163 Self::UnsupportedSchema { token } => write!(
164 f,
165 "unsupported schema {token}: alignment entities require IFC4X3 ADD2"
166 ),
167 Self::SemanticViolation { entity, rule } => match entity {
168 Some(entity) => write!(f, "{entity} violates {rule}"),
169 None => write!(f, "violates {rule}"),
170 },
171 Self::InvalidAuthoredValue {
172 type_name,
173 attribute,
174 detail,
175 } => write!(f, "cannot author {type_name}.{attribute}: {detail}"),
176 Self::DanglingReference {
177 entity,
178 attribute,
179 target,
180 } => write!(f, "{entity}.{attribute} references missing {target}"),
181 Self::BudgetExceeded {
182 max_depth,
183 max_nodes,
184 } => write!(
185 f,
186 "traversal exceeded bounds (max_depth={max_depth}, max_nodes={max_nodes})"
187 ),
188 }
189 }
190}
191
192impl std::error::Error for AlignmentError {}