ifc_structural/error.rs
1//! Structured failures for structural analysis projections and authoring.
2
3use ifc_model::EntityId;
4
5/// Result type for `ifc-structural` operations.
6pub type StructuralResult<T> = Result<T, StructuralError>;
7
8/// Why a structural projection or draft was refused.
9#[derive(Debug, Clone, PartialEq, Eq)]
10#[non_exhaustive]
11pub enum StructuralError {
12 /// The model header declares no `FILE_SCHEMA` token.
13 MissingSchema,
14 /// The header declares more than one schema, so the profile is ambiguous.
15 AmbiguousSchema {
16 /// Every schema token found in the header.
17 tokens: Vec<String>,
18 },
19 /// The declared schema token is not IFC2X3, IFC4, or IFC4X3.
20 UnsupportedSchema {
21 /// The rejected schema token.
22 token: String,
23 },
24 /// An entity id was not found in the model.
25 EntityNotFound {
26 /// The id that resolved to nothing.
27 id: EntityId,
28 },
29 /// An entity was not the IFC type (or a subtype of it) the caller requested.
30 WrongType {
31 /// The entity that was read.
32 id: EntityId,
33 /// The IFC type or supertype required.
34 expected: &'static str,
35 /// The IFC type actually declared.
36 actual: String,
37 },
38 /// The entity's declared type has no attribute of this name in the selected schema.
39 MissingAttribute {
40 /// The entity that was read.
41 entity: EntityId,
42 /// The attribute name that does not exist for this type in this schema version.
43 attribute: &'static str,
44 },
45 /// A staged draft omitted an attribute that is mandatory for its IFC entity type.
46 MissingRequired {
47 /// The IFC entity type being authored.
48 entity_type: String,
49 /// The mandatory attribute that was left unset.
50 attribute: String,
51 },
52 /// A staged draft set an attribute that does not exist for its type in the target schema.
53 UnsupportedAttribute {
54 /// The IFC entity type being authored.
55 entity_type: String,
56 /// The attribute name absent from this schema version.
57 attribute: String,
58 },
59 /// An attribute held a value of the wrong kind or shape for the schema.
60 InvalidValue {
61 /// The entity that was read.
62 entity: EntityId,
63 /// The attribute holding the invalid value.
64 attribute: &'static str,
65 /// The kind of value that was expected instead.
66 expected: &'static str,
67 },
68 /// A staged draft supplied a value of the wrong kind or shape for the attribute.
69 InvalidDraftValue {
70 /// The IFC entity type being authored.
71 entity_type: &'static str,
72 /// The attribute holding the invalid value.
73 attribute: &'static str,
74 /// The kind of value that was expected instead.
75 expected: &'static str,
76 },
77 /// An entity reference attribute points at an id absent from the model.
78 DanglingReference {
79 /// The entity holding the dangling reference.
80 entity: EntityId,
81 /// The attribute holding the dangling reference.
82 attribute: &'static str,
83 /// The id that resolved to nothing.
84 target: EntityId,
85 },
86 /// An entity reference resolved, but the target is not one of the types the slot allows.
87 WrongReferenceType {
88 /// The entity holding the reference.
89 entity: EntityId,
90 /// The attribute holding the reference.
91 attribute: &'static str,
92 /// The entity that was read.
93 target: EntityId,
94 /// The IFC type or select members required.
95 expected: &'static str,
96 /// The IFC type actually declared by the target.
97 actual: String,
98 },
99 /// A SET/LIST attribute had fewer or more members than the schema allows.
100 InvalidCardinality {
101 /// The entity holding the aggregate attribute.
102 entity: EntityId,
103 /// The attribute whose aggregate size is out of bounds.
104 attribute: &'static str,
105 /// Minimum allowed member count.
106 minimum: usize,
107 /// Maximum allowed member count, or `None` if unbounded.
108 maximum: Option<usize>,
109 /// The member count actually found.
110 actual: usize,
111 },
112 /// A structural semantic rule beyond plain schema shape was violated.
113 ///
114 /// Used for constraints the EXPRESS schema cannot encode directly, such
115 /// as forbidding an `IfcRelAssignsToGroup` from assigning its own
116 /// `RelatingGroup`, or requiring `ObjectType` when `PredefinedType` is
117 /// `USERDEFINED`.
118 SemanticViolation {
119 /// The entity that violated the rule, if the violation is entity-scoped.
120 entity: Option<EntityId>,
121 /// A short identifier naming the violated rule.
122 rule: &'static str,
123 },
124 /// `GlobalId` was not a well-formed 22-character IFC GUID.
125 InvalidGlobalId,
126}
127
128impl std::fmt::Display for StructuralError {
129 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130 match self {
131 Self::MissingSchema => f.write_str("the model header declares no IFC schema"),
132 Self::AmbiguousSchema { tokens } => write!(f, "the model declares multiple schemas: {tokens:?}"),
133 Self::UnsupportedSchema { token } => write!(f, "unsupported IFC schema `{token}`"),
134 Self::EntityNotFound { id } => write!(f, "entity {id} does not exist"),
135 Self::WrongType { id, expected, actual } => write!(f, "entity {id} is {actual}, expected {expected}"),
136 Self::MissingAttribute { entity, attribute } => write!(f, "entity {entity} has no `{attribute}` attribute in the selected schema"),
137 Self::MissingRequired { entity_type, attribute } => write!(f, "{entity_type}.{attribute} is required"),
138 Self::UnsupportedAttribute { entity_type, attribute } => write!(f, "{entity_type}.{attribute} does not exist in the selected schema"),
139 Self::InvalidValue { entity, attribute, expected } => write!(f, "entity {entity} has invalid `{attribute}`; expected {expected}"),
140 Self::InvalidDraftValue { entity_type, attribute, expected } => write!(f, "draft {entity_type} has invalid `{attribute}`; expected {expected}"),
141 Self::DanglingReference { entity, attribute, target } => write!(f, "entity {entity}.{attribute} refers to missing {target}"),
142 Self::WrongReferenceType { entity, attribute, target, expected, actual } => write!(f, "entity {entity}.{attribute} refers to {target} ({actual}), expected {expected}"),
143 Self::InvalidCardinality { entity, attribute, minimum, maximum, actual } => write!(f, "entity {entity}.{attribute} cardinality {actual} is outside {minimum}..{maximum:?}"),
144 Self::SemanticViolation { rule, .. } => write!(f, "structural semantic rule `{rule}` failed"),
145 Self::InvalidGlobalId => f.write_str("GlobalId is not a valid 22-character IFC GUID"),
146 }
147 }
148}
149
150impl std::error::Error for StructuralError {}