ifc_structural/load/
reinforcement.rs1use ifc_model::{EntityId, Value};
9
10use crate::error::{StructuralError, StructuralResult};
11use crate::view::Record;
12
13#[derive(Debug, Clone, Copy)]
17pub struct SurfaceReinforcementArea<'m, 's> {
18 record: Record<'m, 's>,
19}
20
21impl<'m, 's> SurfaceReinforcementArea<'m, 's> {
22 pub(crate) fn from_record(record: Record<'m, 's>) -> StructuralResult<Self> {
25 let area = Self { record };
26 let specified = [
27 "SurfaceReinforcement1",
28 "SurfaceReinforcement2",
29 "ShearReinforcement",
30 ]
31 .into_iter()
32 .map(|attribute| {
33 area.record
34 .value(attribute)
35 .map(|value| !matches!(value.unwrap_typed(), Value::Null | Value::Derived))
36 })
37 .collect::<StructuralResult<Vec<_>>>()?;
38 if !specified.contains(&true) {
39 return Err(StructuralError::SemanticViolation {
40 entity: Some(area.record.id),
41 rule: "SurfaceAndOrShearAreaSpecified",
42 });
43 }
44 Ok(area)
45 }
46
47 #[must_use]
48 pub fn id(&self) -> EntityId {
50 self.record.id
51 }
52
53 pub fn name(&self) -> StructuralResult<Option<&'m str>> {
55 self.record.optional_text("Name")
56 }
57
58 pub fn surface_reinforcement_1(&self) -> StructuralResult<Option<Vec<f64>>> {
66 self.area_list("SurfaceReinforcement1", "NonnegativeArea1")
67 }
68
69 pub fn surface_reinforcement_2(&self) -> StructuralResult<Option<Vec<f64>>> {
75 self.area_list("SurfaceReinforcement2", "NonnegativeArea2")
76 }
77
78 pub fn shear_reinforcement(&self) -> StructuralResult<Option<f64>> {
82 let value = self.record.optional_number("ShearReinforcement")?;
83 if value.is_some_and(|number| number < 0.0) {
84 return Err(StructuralError::SemanticViolation {
85 entity: Some(self.record.id),
86 rule: "NonnegativeArea3",
87 });
88 }
89 Ok(value)
90 }
91
92 fn area_list(
93 &self,
94 attribute: &'static str,
95 rule: &'static str,
96 ) -> StructuralResult<Option<Vec<f64>>> {
97 let values = match self.record.value(attribute)?.unwrap_typed() {
98 Value::Null | Value::Derived => return Ok(None),
99 Value::List(values) => values,
100 _ => {
101 return Err(StructuralError::InvalidValue {
102 entity: self.record.id,
103 attribute,
104 expected: "LIST [2:3] OF IfcLengthMeasure or null",
105 })
106 }
107 };
108 if !(2..=3).contains(&values.len()) {
109 return Err(StructuralError::InvalidCardinality {
110 entity: self.record.id,
111 attribute,
112 minimum: 2,
113 maximum: Some(3),
114 actual: values.len(),
115 });
116 }
117 let numbers = values
118 .iter()
119 .map(|value| match value.unwrap_typed() {
120 Value::Integer(number) => Ok(*number as f64),
121 Value::Real(number) if number.is_finite() => Ok(*number),
122 _ => Err(StructuralError::InvalidValue {
123 entity: self.record.id,
124 attribute,
125 expected: "LIST [2:3] OF finite IfcLengthMeasure",
126 }),
127 })
128 .collect::<StructuralResult<Vec<f64>>>()?;
129 if numbers[..2].iter().any(|number| *number < 0.0) {
130 return Err(StructuralError::SemanticViolation {
131 entity: Some(self.record.id),
132 rule,
133 });
134 }
135 Ok(Some(numbers))
136 }
137}