ifc_properties/template/
property_set.rs1use std::collections::BTreeMap;
17use std::sync::Arc;
18
19use ifc_model::{EntityId, Model, Value};
20
21use super::layout::Layout;
22use super::property::{member_templates, PropertyTemplate};
23use crate::error::{PropertyAnomaly, TemplateError};
24use crate::nesting::Nesting;
25
26#[derive(Debug, Clone, PartialEq)]
28pub struct PropertySetTemplate {
29 pub id: EntityId,
31 pub name: Option<Arc<str>>,
33 pub description: Option<Arc<str>>,
35 pub template_type: Option<Arc<str>>,
37 pub applicable_entity: Option<Arc<str>>,
41 pub properties: Vec<PropertyTemplate>,
44}
45
46impl PropertySetTemplate {
47 pub fn property(&self, name: &str) -> Option<&PropertyTemplate> {
49 self.properties
50 .iter()
51 .find(|p| p.name.as_deref() == Some(name))
52 }
53}
54
55pub fn property_set_template(model: &Model, id: EntityId) -> Option<PropertySetTemplate> {
62 let mut anomalies = Vec::new();
63 read_set_template(model, Layout::permissive(model), id, &mut anomalies)
64}
65
66pub fn property_set_template_checked(
81 model: &Model,
82 id: EntityId,
83) -> Result<(PropertySetTemplate, Vec<PropertyAnomaly>), TemplateError> {
84 let layout = Layout::declared(model)?;
85 let entity = model.get(id).ok_or(TemplateError::MissingEntity { id })?;
86 let mut anomalies = Vec::new();
87 let template = read_set_template(model, layout, id, &mut anomalies).ok_or(
88 TemplateError::NotATemplate {
89 id,
90 type_name: entity.type_name.to_string(),
91 },
92 )?;
93 Ok((template, anomalies))
94}
95
96pub(crate) fn read_set_template(
97 model: &Model,
98 layout: Layout,
99 id: EntityId,
100 anomalies: &mut Vec<PropertyAnomaly>,
101) -> Option<PropertySetTemplate> {
102 let entity = model.get(id)?;
103 if !layout.is_a(&entity.type_name, "IFCPROPERTYSETTEMPLATE") {
104 return None;
105 }
106 layout.check_arity(id, entity, anomalies);
107 let mut template = PropertySetTemplate {
108 id,
109 name: layout.text(id, entity, "Name", anomalies),
110 description: layout.text(id, entity, "Description", anomalies),
111 template_type: layout.enumeration(id, entity, "TemplateType", anomalies),
112 applicable_entity: layout.text(id, entity, "ApplicableEntity", anomalies),
113 properties: Vec::new(),
114 };
115 let mut nesting = Nesting::new(anomalies);
116 let members = layout.get(entity, "HasPropertyTemplates");
117 template.properties = member_templates(model, layout, id, members, &mut nesting);
118 Some(template)
119}
120
121pub fn property_set_templates(model: &Model) -> Vec<PropertySetTemplate> {
123 let mut ids: Vec<_> = model.ids_of_type("IFCPROPERTYSETTEMPLATE").to_vec();
124 ids.sort_unstable();
125 ids.into_iter()
126 .filter_map(|id| property_set_template(model, id))
127 .collect()
128}
129
130pub fn template_of_set(model: &Model) -> BTreeMap<EntityId, Vec<EntityId>> {
141 template_links(model, Layout::permissive(model))
142 .into_iter()
143 .map(|(set, links)| {
144 let mut templates: Vec<EntityId> = links.into_iter().map(|(_, t)| t).collect();
145 templates.sort_unstable();
146 templates.dedup();
147 (set, templates)
148 })
149 .collect()
150}
151
152pub(crate) fn template_links(
155 model: &Model,
156 layout: Layout,
157) -> BTreeMap<EntityId, Vec<(EntityId, EntityId)>> {
158 let mut out: BTreeMap<EntityId, Vec<(EntityId, EntityId)>> = BTreeMap::new();
159 let mut relationships = model.ids_of_type("IFCRELDEFINESBYTEMPLATE").to_vec();
160 relationships.sort_unstable();
161 for id in relationships {
162 let Some(rel) = model.get(id) else { continue };
163 let Some(template) = layout.get(rel, "RelatingTemplate").and_then(one_ref) else {
164 continue;
165 };
166 let sets = match layout.get(rel, "RelatedPropertySets") {
167 Some(Value::List(items)) => items.iter().filter_map(one_ref).collect(),
168 _ => Vec::new(),
169 };
170 for set in sets {
171 out.entry(set).or_default().push((id, template));
172 }
173 }
174 out
175}
176
177fn one_ref(value: &Value) -> Option<EntityId> {
178 match value.unwrap_typed() {
179 Value::Ref(id) => Some(*id),
180 _ => None,
181 }
182}