Skip to main content

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 {}