Skip to main content

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