1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
//! Structured failures for resource projections, queries, and authoring.
use ifc_model::EntityId;
/// Result of a resource projection, query, or authoring call, carrying
/// [`ResourceError`] on failure.
pub type ResourceResult<T> = Result<T, ResourceError>;
/// Why a resource projection, query, or authoring call could not be
/// completed.
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ResourceError {
/// The model header declares no `FILE_SCHEMA` token.
MissingSchema,
/// The header declares more than one schema, so the profile is ambiguous.
AmbiguousSchema {
/// Every schema token found in the header.
tokens: Vec<String>,
},
/// The declared schema is not IFC4 ADD2 TC1 or IFC4X3 ADD2.
UnsupportedSchema {
/// The rejected schema token.
token: String,
},
/// A requested entity id does not exist in the model.
EntityNotFound {
/// The missing entity id.
id: EntityId,
},
/// An entity was not the IFC type the caller requested.
WrongType {
/// The entity that was read.
id: EntityId,
/// The IFC type the caller expected.
expected: &'static str,
/// The IFC type actually declared.
actual: String,
},
/// The schema declares no attribute of this name on the entity type.
MissingAttribute {
/// The entity that was read.
entity: EntityId,
/// Schema name of the absent attribute.
attribute: &'static str,
},
/// An attribute was present but held the wrong kind of value.
InvalidValue {
/// The entity that was read.
entity: EntityId,
/// Schema name of the offending attribute.
attribute: &'static str,
/// What kind of value was expected.
expected: &'static str,
},
/// An enumeration attribute held a value the schema does not declare.
InvalidEnumeration {
/// The entity that was read, when the value came from an entity
/// rather than an authoring draft.
entity: Option<EntityId>,
/// Schema name of the offending attribute.
attribute: &'static str,
/// The undeclared value.
value: String,
},
/// An entity reference attribute pointed at an id the model does not
/// contain.
DanglingReference {
/// The entity holding the dangling reference.
entity: EntityId,
/// Schema name of the offending attribute.
attribute: &'static str,
/// The id that resolved to nothing.
target: EntityId,
},
/// An entity reference resolved, but the target is not the IFC type the
/// slot requires.
WrongReferenceType {
/// The entity holding the reference.
entity: EntityId,
/// Schema name of the offending attribute.
attribute: &'static str,
/// The referenced entity.
target: EntityId,
/// The IFC type the slot requires.
expected: &'static str,
/// The IFC type actually declared.
actual: String,
},
/// An aggregate attribute held fewer items than the schema's minimum
/// cardinality.
InvalidCardinality {
/// The entity that was read.
entity: EntityId,
/// Schema name of the offending attribute.
attribute: &'static str,
/// The schema's minimum cardinality.
minimum: usize,
/// The actual number of items found.
actual: usize,
},
/// A `SET` attribute repeated the same reference more than once.
DuplicateReference {
/// The entity that was read.
entity: EntityId,
/// Schema name of the offending attribute.
attribute: &'static str,
/// The repeated reference.
target: EntityId,
},
/// A WHERE rule or other cross-attribute IFC semantic constraint failed.
SemanticViolation {
/// The entity that violated the rule, when applicable.
entity: Option<EntityId>,
/// Name of the violated rule.
rule: &'static str,
},
/// A resource composition or nesting traversal revisited an entity.
Cycle {
/// The entity where the cycle was detected.
at: EntityId,
},
/// A bounded traversal exceeded its depth or node budget.
BudgetExceeded {
/// The maximum depth allowed.
max_depth: usize,
/// The maximum number of nodes allowed.
max_nodes: usize,
},
/// A staged authoring draft could not be committed as written.
InvalidDraft {
/// The IFC entity type being authored.
entity_type: &'static str,
/// Schema name of the offending attribute.
attribute: &'static str,
/// What kind of value was expected.
expected: &'static str,
},
/// A supplied `GlobalId` is not a valid 22-character compressed IFC GUID.
InvalidGlobalId,
/// A transaction commit conflicted with a concurrent revision.
TransactionConflict {
/// The revision the transaction expected.
expected: u64,
/// The revision actually current.
actual: u64,
},
}
impl std::fmt::Display for ResourceError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::MissingSchema => f.write_str("the model header declares no IFC schema"),
Self::AmbiguousSchema { tokens } => {
write!(f, "the model declares multiple schemas: {tokens:?}")
}
Self::UnsupportedSchema { token } => {
write!(
f,
"unsupported resource schema `{token}`; expected IFC4 ADD2 TC1 or IFC4X3 ADD2"
)
}
Self::EntityNotFound { id } => write!(f, "entity {id} does not exist"),
Self::WrongType {
id,
expected,
actual,
} => {
write!(f, "entity {id} is {actual}, expected {expected}")
}
Self::MissingAttribute { entity, attribute } => {
write!(f, "entity {entity} has no `{attribute}` slot")
}
Self::InvalidValue {
entity,
attribute,
expected,
} => {
write!(
f,
"entity {entity}.{attribute} is invalid; expected {expected}"
)
}
Self::InvalidEnumeration {
entity,
attribute,
value,
} => match entity {
Some(entity) => write!(
f,
"entity {entity}.{attribute} has undeclared enumeration `{value}`"
),
None => write!(
f,
"resource draft.{attribute} has undeclared enumeration `{value}`"
),
},
Self::DanglingReference {
entity,
attribute,
target,
} => {
write!(f, "entity {entity}.{attribute} refers to missing {target}")
}
Self::WrongReferenceType {
entity,
attribute,
target,
expected,
actual,
} => write!(
f,
"entity {entity}.{attribute} refers to {target} ({actual}), expected {expected}"
),
Self::InvalidCardinality {
entity,
attribute,
minimum,
actual,
} => write!(
f,
"entity {entity}.{attribute} has {actual} items; minimum is {minimum}"
),
Self::DuplicateReference {
entity,
attribute,
target,
} => write!(f, "entity {entity}.{attribute} repeats reference {target}"),
Self::SemanticViolation { rule, .. } => {
write!(f, "resource semantic rule `{rule}` failed")
}
Self::Cycle { at } => write!(f, "resource composition cycle revisits {at}"),
Self::BudgetExceeded {
max_depth,
max_nodes,
} => write!(
f,
"resource traversal exceeded depth {max_depth} or node budget {max_nodes}"
),
Self::InvalidDraft {
entity_type,
attribute,
expected,
} => write!(
f,
"draft {entity_type}.{attribute} is invalid; expected {expected}"
),
Self::InvalidGlobalId => {
f.write_str("GlobalId is not a valid 22-character compressed IFC GUID")
}
Self::TransactionConflict { expected, actual } => write!(
f,
"resource transaction revision conflict: expected {expected}, actual {actual}"
),
}
}
}
impl std::error::Error for ResourceError {}