Skip to main content

ifc_classification/classification/
notation.rs

1//! Borrowed IFC2X3 `IfcClassificationNotation` projection.
2//!
3//! IFC2X3 lets `IfcRelAssociatesClassification.RelatingClassification` name
4//! a notation instead of a reference. A notation is a set of facets, and its
5//! code is the facets' `NotationValue`s in the order the file lists them.
6//! This crate does not join them: the separator is a property of the
7//! classification system, not of the file. IFC4 removed both records, so on
8//! an IFC4 model every read here is `NotInSchema`.
9
10use ifc_model::EntityId;
11
12use crate::view::{borrowed_entity, required_refs, required_text, ClassificationView};
13use crate::{ClassificationError, ClassificationResult};
14
15const NOTATION: &str = "IFCCLASSIFICATIONNOTATION";
16const FACET: &str = "IFCCLASSIFICATIONNOTATIONFACET";
17
18borrowed_entity!(ClassificationNotation, "IFCCLASSIFICATIONNOTATION");
19
20impl ClassificationNotation<'_> {
21    /// Ids of the `NotationFacets`, in file order; non-empty per schema.
22    pub fn facet_ids(self) -> ClassificationResult<Vec<EntityId>> {
23        required_refs(
24            NOTATION,
25            self.id(),
26            self.entity(),
27            self.slot("NotationFacets")?,
28            "NotationFacets",
29        )
30    }
31}
32
33impl<'m> ClassificationView<'m> {
34    /// All `IfcClassificationNotation` instances in the model (IFC2X3 only).
35    pub fn notations(self) -> impl Iterator<Item = ClassificationNotation<'m>> + 'm {
36        self.model()
37            .of_type(NOTATION)
38            .map(move |(id, e)| ClassificationNotation::from_known(id, e, self.release()))
39    }
40
41    /// Strictly project the `IfcClassificationNotation` `id`.
42    ///
43    /// # Errors
44    ///
45    /// `UnknownEntity`, `WrongEntityType`, or `NotInSchema` when the bound
46    /// release (IFC4) does not define notations.
47    pub fn notation(self, id: EntityId) -> ClassificationResult<ClassificationNotation<'m>> {
48        let entity = self
49            .model()
50            .get(id)
51            .ok_or(ClassificationError::UnknownEntity { id })?;
52        let notation = ClassificationNotation::try_bound(id, entity, self.release())?;
53        notation.slot("NotationFacets")?;
54        Ok(notation)
55    }
56
57    /// The notation's code: each facet's `NotationValue`, in file order.
58    ///
59    /// # Errors
60    ///
61    /// Those of [`Self::notation`], plus a dangling facet, a facet that is not
62    /// an `IfcClassificationNotationFacet`, or a facet without a value.
63    pub fn notation_values(self, id: EntityId) -> ClassificationResult<Vec<&'m str>> {
64        let notation = self.notation(id)?;
65        let mut values = Vec::new();
66        for facet_id in notation.facet_ids()? {
67            let facet =
68                self.model()
69                    .get(facet_id)
70                    .ok_or(ClassificationError::DanglingReference {
71                        entity: NOTATION,
72                        id,
73                        attribute: "NotationFacets",
74                        target: facet_id,
75                    })?;
76            if !facet.is_type(FACET) {
77                return Err(ClassificationError::ReferenceType {
78                    entity: NOTATION,
79                    id,
80                    attribute: "NotationFacets",
81                    target: facet_id,
82                    expected: "IfcClassificationNotationFacet",
83                    actual: facet.type_name.to_string(),
84                });
85            }
86            let slot = self
87                .release()
88                .text_slot(FACET, facet_id, facet, "NotationValue")?;
89            values.push(required_text(
90                FACET,
91                facet_id,
92                facet,
93                slot,
94                "NotationValue",
95            )?);
96        }
97        Ok(values)
98    }
99}