Skip to main content

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}