ifc_structural/load/
dynamic.rs1use ifc_model::{EntityId, Value};
4
5use crate::error::{StructuralError, StructuralResult};
6use crate::view::Record;
7
8#[derive(Debug, Clone, Copy)]
13pub struct LoadConfiguration<'m, 's> {
14 record: Record<'m, 's>,
15}
16
17impl<'m, 's> LoadConfiguration<'m, 's> {
18 pub(crate) fn from_record(record: Record<'m, 's>) -> Self {
19 Self { record }
20 }
21
22 #[must_use]
24 pub fn id(&self) -> EntityId {
25 self.record.id
26 }
27
28 pub fn name(&self) -> StructuralResult<Option<&'m str>> {
30 self.record.optional_text("Name")
31 }
32
33 pub fn values(&self) -> StructuralResult<Vec<EntityId>> {
35 let values = match self.record.value("Values")?.unwrap_typed() {
36 Value::List(values) if !values.is_empty() => values,
37 Value::List(values) => {
38 return Err(StructuralError::InvalidCardinality {
39 entity: self.record.id,
40 attribute: "Values",
41 minimum: 1,
42 maximum: None,
43 actual: values.len(),
44 })
45 }
46 _ => {
47 return Err(StructuralError::InvalidValue {
48 entity: self.record.id,
49 attribute: "Values",
50 expected: "LIST [1:?] of IfcStructuralLoadOrResult references",
51 })
52 }
53 };
54 values
55 .iter()
56 .map(|value| self.load_or_result_ref(value))
57 .collect()
58 }
59
60 pub fn locations(&self) -> StructuralResult<Option<Vec<Vec<f64>>>> {
65 let raw = match self.record.value("Locations")?.unwrap_typed() {
66 Value::Null | Value::Derived => return Ok(None),
67 Value::List(values) if !values.is_empty() => values,
68 Value::List(values) => {
69 return Err(StructuralError::InvalidCardinality {
70 entity: self.record.id,
71 attribute: "Locations",
72 minimum: 1,
73 maximum: None,
74 actual: values.len(),
75 })
76 }
77 _ => {
78 return Err(StructuralError::InvalidValue {
79 entity: self.record.id,
80 attribute: "Locations",
81 expected: "LIST [1:?] of unique LIST [1:2] finite lengths",
82 })
83 }
84 };
85 let value_count = self.values()?.len();
86 if raw.len() != value_count {
87 return Err(StructuralError::SemanticViolation {
88 entity: Some(self.record.id),
89 rule: "IfcStructuralLoadConfiguration.ValidListSize",
90 });
91 }
92 let mut locations = Vec::with_capacity(raw.len());
93 for raw_location in raw {
94 let values = match raw_location.unwrap_typed() {
95 Value::List(values) if (1..=2).contains(&values.len()) => values,
96 Value::List(values) => {
97 return Err(StructuralError::InvalidCardinality {
98 entity: self.record.id,
99 attribute: "Locations",
100 minimum: 1,
101 maximum: Some(2),
102 actual: values.len(),
103 })
104 }
105 _ => {
106 return Err(StructuralError::InvalidValue {
107 entity: self.record.id,
108 attribute: "Locations",
109 expected: "LIST [1:2] of finite length measures",
110 })
111 }
112 };
113 let mut location = Vec::with_capacity(values.len());
114 for value in values {
115 let number = match value.unwrap_typed() {
116 Value::Integer(value) => *value as f64,
117 Value::Real(value) => *value,
118 _ => {
119 return Err(StructuralError::InvalidValue {
120 entity: self.record.id,
121 attribute: "Locations",
122 expected: "finite length measure",
123 })
124 }
125 };
126 if !number.is_finite() {
127 return Err(StructuralError::InvalidValue {
128 entity: self.record.id,
129 attribute: "Locations",
130 expected: "finite length measure",
131 });
132 }
133 location.push(number);
134 }
135 if locations.contains(&location) {
136 return Err(StructuralError::SemanticViolation {
137 entity: Some(self.record.id),
138 rule: "IfcStructuralLoadConfiguration.UniqueLocations",
139 });
140 }
141 locations.push(location);
142 }
143 Ok(Some(locations))
144 }
145
146 fn load_or_result_ref(&self, value: &Value) -> StructuralResult<EntityId> {
147 let Value::Ref(id) = value.unwrap_typed() else {
148 return Err(StructuralError::InvalidValue {
149 entity: self.record.id,
150 attribute: "Values",
151 expected: "IfcStructuralLoadOrResult reference",
152 });
153 };
154 let target = self
155 .record
156 .model
157 .get(*id)
158 .ok_or(StructuralError::DanglingReference {
159 entity: self.record.id,
160 attribute: "Values",
161 target: *id,
162 })?;
163 if !self
164 .record
165 .schema
166 .is_a(&target.type_name, "IfcStructuralLoadOrResult")
167 {
168 return Err(StructuralError::WrongType {
169 id: *id,
170 expected: "IfcStructuralLoadOrResult",
171 actual: target.type_name.to_string(),
172 });
173 }
174 Ok(*id)
175 }
176}