ifc_schema/entity.rs
1//! Entity descriptors used by the IFC schema registry.
2
3use crate::attribute::{Aggregation, Attribute};
4
5/// One `INVERSE` attribute: `Name : SET [0:1] OF Entity FOR Attribute;`.
6///
7/// Inverse attributes occupy no Part 21 slot; they state how many instances
8/// of `entity` may point at this one through `for_attribute`.
9#[derive(Debug, Clone, PartialEq, Eq)]
10#[non_exhaustive]
11pub struct InverseAttribute {
12 /// Declared name, unqualified.
13 pub name: String,
14 /// The supertype named when a subtype redeclares an inherited inverse
15 /// (`SELF\X.Name : ...`); `None` for a new inverse attribute.
16 pub redeclares: Option<String>,
17 /// The entity whose attribute points back at this one.
18 pub entity: String,
19 /// The attribute named after `FOR`, as written.
20 pub for_attribute: String,
21 /// `SET` or `BAG` with its bounds; `None` for a single-valued inverse.
22 pub aggregation: Option<Aggregation>,
23}
24
25impl InverseAttribute {
26 /// Creates a single-valued inverse attribute.
27 #[must_use]
28 pub fn new(
29 name: impl Into<String>,
30 entity: impl Into<String>,
31 for_attribute: impl Into<String>,
32 ) -> Self {
33 Self {
34 name: name.into(),
35 redeclares: None,
36 entity: entity.into(),
37 for_attribute: for_attribute.into(),
38 aggregation: None,
39 }
40 }
41
42 /// Marks the inverse a redeclaration of `supertype`'s.
43 #[must_use]
44 pub fn redeclaring(mut self, supertype: impl Into<String>) -> Self {
45 self.redeclares = Some(supertype.into());
46 self
47 }
48
49 /// Makes the inverse an aggregate (`SET` or `BAG`).
50 #[must_use]
51 pub fn with_aggregation(mut self, aggregation: Aggregation) -> Self {
52 self.aggregation = Some(aggregation);
53 self
54 }
55}
56
57/// One `UNIQUE` rule: the named attributes are unique, jointly, across
58/// every instance of the declaring entity and its subtypes.
59#[derive(Debug, Clone, PartialEq, Eq)]
60#[non_exhaustive]
61pub struct UniqueRule {
62 /// Rule label, e.g. `UR1`; `None` when the rule is unlabelled.
63 pub label: Option<String>,
64 /// Attribute names as written; a qualified name (`SELF\X.Y`) is kept.
65 pub attributes: Vec<String>,
66}
67
68impl UniqueRule {
69 /// Creates a labelled rule over `attributes`.
70 #[must_use]
71 pub fn new(label: impl Into<String>, attributes: Vec<String>) -> Self {
72 Self {
73 label: Some(label.into()),
74 attributes,
75 }
76 }
77
78 /// Creates an unlabelled rule over `attributes`.
79 #[must_use]
80 pub const fn unlabelled(attributes: Vec<String>) -> Self {
81 Self {
82 label: None,
83 attributes,
84 }
85 }
86}
87
88/// One `WHERE` rule: a named constraint an instance must satisfy.
89///
90/// The bundled tables carry the **label** only; [`Self::expression`] is empty
91/// for them (the expressions are CC BY-ND schema text, see
92/// `data/NOTICE.md`). A schema parsed from EXPRESS source with the `express`
93/// feature keeps the whitespace-normalised expression.
94#[derive(Debug, Clone, PartialEq, Eq)]
95#[non_exhaustive]
96pub struct WhereRule {
97 /// Rule label as declared, e.g. `CurveIs3D`.
98 pub label: String,
99 /// Constraint expression as written, or empty when not recorded.
100 pub expression: String,
101}
102
103impl WhereRule {
104 /// Creates a rule from its label and expression text.
105 #[must_use]
106 pub fn new(label: impl Into<String>, expression: impl Into<String>) -> Self {
107 Self {
108 label: label.into(),
109 expression: expression.into(),
110 }
111 }
112}
113
114/// One structural entity declaration.
115///
116/// `#[non_exhaustive]`: build one with [`EntityDef::new`] and the builder
117/// methods. Declaration facts not yet recorded are added as new fields
118/// without breaking readers.
119#[derive(Debug, Clone, PartialEq, Eq)]
120#[non_exhaustive]
121pub struct EntityDef {
122 /// Declared entity name.
123 pub name: String,
124 /// Direct supertypes in `SUBTYPE OF` order; empty for a root entity.
125 ///
126 /// IFC is single-inheritance, so this holds at most one name for every
127 /// bundled schema; [`Self::supertype`] reads it.
128 pub supertypes: Vec<String>,
129 /// Whether the declaration includes `ABSTRACT`.
130 pub abstract_: bool,
131 /// Explicit attributes declared by this entity, excluding derived and
132 /// inverse declarations and explicit redeclarations of inherited ones.
133 pub attributes: Vec<Attribute>,
134 /// Names of attributes this entity declares in its `DERIVE` block,
135 /// unqualified (`SELF\Entity.` stripped), in declaration order.
136 ///
137 /// A redeclared inherited attribute keeps its position but is written
138 /// `*` in a Part 21 record. New derived attributes appear here too; they
139 /// occupy no positional slot.
140 pub derived: Vec<String>,
141 /// `WHERE` rules declared by this entity (not inherited ones), in
142 /// declaration order.
143 pub where_rules: Vec<WhereRule>,
144 /// `INVERSE` attributes declared by this entity, in declaration order.
145 /// Empty in a table written before they were recorded (artifact format
146 /// 1 or 2).
147 pub inverses: Vec<InverseAttribute>,
148 /// `UNIQUE` rules declared by this entity (not inherited ones), in
149 /// declaration order. Empty in a table written before they were
150 /// recorded (artifact format 1 or 2).
151 pub unique_rules: Vec<UniqueRule>,
152}
153
154impl EntityDef {
155 /// Creates an empty concrete entity declaration.
156 #[must_use]
157 pub fn new(name: impl Into<String>) -> Self {
158 Self {
159 name: name.into(),
160 supertypes: Vec::new(),
161 abstract_: false,
162 attributes: Vec::new(),
163 derived: Vec::new(),
164 where_rules: Vec::new(),
165 inverses: Vec::new(),
166 unique_rules: Vec::new(),
167 }
168 }
169
170 /// Appends an `INVERSE` attribute in declaration order.
171 #[must_use]
172 pub fn with_inverse(mut self, inverse: InverseAttribute) -> Self {
173 self.inverses.push(inverse);
174 self
175 }
176
177 /// Appends a `UNIQUE` rule in declaration order.
178 #[must_use]
179 pub fn with_unique_rule(mut self, rule: UniqueRule) -> Self {
180 self.unique_rules.push(rule);
181 self
182 }
183
184 /// Appends a direct supertype, after any already declared.
185 #[must_use]
186 pub fn with_supertype(mut self, supertype: impl Into<String>) -> Self {
187 self.supertypes.push(supertype.into());
188 self
189 }
190
191 /// Marks the declaration `ABSTRACT`.
192 #[must_use]
193 pub const fn abstract_entity(mut self) -> Self {
194 self.abstract_ = true;
195 self
196 }
197
198 /// Appends an explicit attribute in declaration order.
199 #[must_use]
200 pub fn with_attribute(mut self, attribute: Attribute) -> Self {
201 self.attributes.push(attribute);
202 self
203 }
204
205 /// Declares an attribute name as derived, as a `DERIVE` block would.
206 #[must_use]
207 pub fn with_derived(mut self, name: impl Into<String>) -> Self {
208 self.derived.push(name.into());
209 self
210 }
211
212 /// Appends a `WHERE` rule in declaration order.
213 #[must_use]
214 pub fn with_where_rule(mut self, rule: WhereRule) -> Self {
215 self.where_rules.push(rule);
216 self
217 }
218
219 /// The first direct supertype, if any.
220 #[must_use]
221 pub fn supertype(&self) -> Option<&str> {
222 self.supertypes.first().map(String::as_str)
223 }
224
225 /// Whether `name` is declared derived by this entity.
226 ///
227 /// Comparison is ASCII case-insensitive.
228 #[must_use]
229 pub fn is_derived(&self, name: &str) -> bool {
230 self.derived
231 .iter()
232 .any(|declared| declared.eq_ignore_ascii_case(name))
233 }
234}