ifc_structural/action/
varying.rs1use ifc_model::{EntityId, Value};
10
11use crate::action::StructuralAction;
12use crate::error::{StructuralError, StructuralResult};
13use crate::load::StaticLoad;
14use crate::view::Record;
15
16const LINEAR_VARYING: &str = "IfcStructuralLinearActionVarying";
17const PLANAR_VARYING: &str = "IfcStructuralPlanarActionVarying";
18
19impl<'m, 's> StructuralAction<'m, 's> {
20 #[must_use]
25 pub fn is_varying(&self) -> bool {
26 self.subsequent_minimum().is_some()
27 }
28
29 pub fn varying_applied_load_location(&self) -> StructuralResult<Option<EntityId>> {
37 if !self.is_varying() {
38 return Ok(None);
39 }
40 self.validate_semantics()?;
41 self.record
42 .required_ref("VaryingAppliedLoadLocation", "IfcShapeAspect")
43 .map(Some)
44 }
45
46 pub fn subsequent_applied_loads(&self) -> StructuralResult<Option<Vec<StaticLoad<'m, 's>>>> {
58 let Some(minimum) = self.subsequent_minimum() else {
59 return Ok(None);
60 };
61 self.validate_semantics()?;
62 const ATTRIBUTE: &str = "SubsequentAppliedLoads";
63 const EXPECTED: &str = "LIST of IfcStructuralLoad references";
64 let values = match self.record.value(ATTRIBUTE)?.unwrap_typed() {
65 Value::List(values) => values,
66 _ => {
67 return Err(StructuralError::InvalidValue {
68 entity: self.record.id,
69 attribute: ATTRIBUTE,
70 expected: EXPECTED,
71 })
72 }
73 };
74 if values.len() < minimum {
75 return Err(StructuralError::InvalidCardinality {
76 entity: self.record.id,
77 attribute: ATTRIBUTE,
78 minimum,
79 maximum: None,
80 actual: values.len(),
81 });
82 }
83 values
84 .iter()
85 .map(|value| {
86 let Value::Ref(target) = value.unwrap_typed() else {
87 return Err(StructuralError::InvalidValue {
88 entity: self.record.id,
89 attribute: ATTRIBUTE,
90 expected: EXPECTED,
91 });
92 };
93 self.subsequent_load(ATTRIBUTE, *target)
94 })
95 .collect::<StructuralResult<Vec<_>>>()
96 .map(Some)
97 }
98
99 fn subsequent_load(
100 &self,
101 attribute: &'static str,
102 target: EntityId,
103 ) -> StructuralResult<StaticLoad<'m, 's>> {
104 let record = Record::new(
105 self.record.model,
106 self.record.schema,
107 target,
108 "IfcStructuralLoad",
109 )
110 .map_err(|error| match error {
111 StructuralError::EntityNotFound { .. } => StructuralError::DanglingReference {
112 entity: self.record.id,
113 attribute,
114 target,
115 },
116 StructuralError::WrongType { actual, .. } => StructuralError::WrongReferenceType {
117 entity: self.record.id,
118 attribute,
119 target,
120 expected: "IfcStructuralLoad",
121 actual,
122 },
123 other => other,
124 })?;
125 StaticLoad::from_record(record)
126 }
127
128 fn subsequent_minimum(&self) -> Option<usize> {
131 let schema = self.record.schema;
132 let type_name = &self.record.entity.type_name;
133 if schema.entity(LINEAR_VARYING).is_some() && schema.is_a(type_name, LINEAR_VARYING) {
134 Some(1)
135 } else if schema.entity(PLANAR_VARYING).is_some() && schema.is_a(type_name, PLANAR_VARYING)
136 {
137 Some(2)
138 } else {
139 None
140 }
141 }
142}