Skip to main content

ifc_material/constituent/
definition.rs

1//! `IfcMaterialConstituent` semantics.
2//!
3//! IFC4 onwards. IFC2X3 declares no constituent, so every accessor on a
4//! record in an IFC2X3 model fails with `EntityNotInSchema`.
5
6use ifc_model::EntityId;
7
8use crate::view::{borrowed_entity, optional_number, optional_text, required_ref, MaterialView};
9use crate::{MaterialError, MaterialResult};
10
11borrowed_entity!(MaterialConstituent, "IFCMATERIALCONSTITUENT");
12
13impl<'m> MaterialConstituent<'m> {
14    /// `IfcMaterialConstituent.Name`, if given.
15    pub fn name(self) -> MaterialResult<Option<&'m str>> {
16        optional_text(
17            "IFCMATERIALCONSTITUENT",
18            self.id(),
19            self.entity(),
20            self.slot("Name")?,
21            "Name",
22        )
23    }
24
25    /// `IfcMaterialConstituent.Description`, if given.
26    pub fn description(self) -> MaterialResult<Option<&'m str>> {
27        optional_text(
28            "IFCMATERIALCONSTITUENT",
29            self.id(),
30            self.entity(),
31            self.slot("Description")?,
32            "Description",
33        )
34    }
35
36    /// `IfcMaterialConstituent.Material`. Required.
37    pub fn material_id(self) -> MaterialResult<EntityId> {
38        required_ref(
39            "IFCMATERIALCONSTITUENT",
40            self.id(),
41            self.entity(),
42            self.slot("Material")?,
43            "Material",
44        )
45    }
46
47    /// `IfcMaterialConstituent.Fraction`, if given. Must be in `0.0..=1.0`
48    /// when present.
49    pub fn fraction(self) -> MaterialResult<Option<f64>> {
50        let value = optional_number(
51            "IFCMATERIALCONSTITUENT",
52            self.id(),
53            self.entity(),
54            self.slot("Fraction")?,
55            "Fraction",
56        )?;
57        if value.is_some_and(|value| !(0.0..=1.0).contains(&value)) {
58            return Err(MaterialError::InvalidValue {
59                entity: "IFCMATERIALCONSTITUENT",
60                id: self.id(),
61                attribute: "Fraction",
62                value: "expected a normalized ratio in 0..=1".to_owned(),
63            });
64        }
65        Ok(value)
66    }
67
68    /// `IfcMaterialConstituent.Category`, if given.
69    pub fn category(self) -> MaterialResult<Option<&'m str>> {
70        optional_text(
71            "IFCMATERIALCONSTITUENT",
72            self.id(),
73            self.entity(),
74            self.slot("Category")?,
75            "Category",
76        )
77    }
78}
79
80impl<'m> MaterialView<'m> {
81    /// Iterates every `IfcMaterialConstituent` instance in the model.
82    pub fn constituents(self) -> impl Iterator<Item = MaterialConstituent<'m>> + 'm {
83        let release = self.release();
84        self.model()
85            .of_type("IFCMATERIALCONSTITUENT")
86            .map(move |(id, entity)| MaterialConstituent::from_known(id, entity, release))
87    }
88}