use std::collections::BTreeMap;
use ifc_properties::{QuantityKind, SchemaVersion};
use super::edit::PropertyEditFailure;
#[cfg_attr(not(feature = "property-catalog"), allow(dead_code))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum TemplateForm {
Single,
Enumerated,
List,
Quantity(QuantityKind),
Other(&'static str),
}
#[derive(Debug, Clone)]
pub(super) struct MemberTemplate {
pub(super) form: TemplateForm,
pub(super) data_type: Option<String>,
pub(super) values: Vec<String>,
}
#[cfg_attr(not(feature = "property-catalog"), allow(dead_code))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum Context {
Any,
TypeOnly,
OccurrenceOnly,
}
#[derive(Debug, Clone)]
pub(super) struct SetTemplate {
pub(super) name: String,
pub(super) quantity: bool,
pub(super) context: Context,
pub(super) members: BTreeMap<String, MemberTemplate>,
}
pub(super) fn reserved(set: &str) -> bool {
let prefix = |p: &str| {
set.get(..p.len())
.is_some_and(|s| s.eq_ignore_ascii_case(p))
};
prefix("Pset_") || prefix("Qto_")
}
pub(super) fn template(
version: SchemaVersion,
set: &str,
) -> Result<Option<SetTemplate>, PropertyEditFailure> {
if !reserved(set) {
return Ok(None);
}
lookup(version, set)
}
#[cfg(not(feature = "property-catalog"))]
fn lookup(_: SchemaVersion, set: &str) -> Result<Option<SetTemplate>, PropertyEditFailure> {
Err(PropertyEditFailure::CatalogUnavailable(set.to_owned()))
}
#[cfg(feature = "property-catalog")]
fn lookup(version: SchemaVersion, set: &str) -> Result<Option<SetTemplate>, PropertyEditFailure> {
use ifc_template_catalog::catalog::CatalogProfile;
use ifc_template_catalog::definition::{
CatalogEdition, PropertyKind, PropertySetType, QuantityKind as Qto, QuantitySetType,
SetTemplateKind,
};
use ifc_template_catalog::embedded::load_catalog;
let (edition, profile) = match version {
SchemaVersion::Ifc2x3 => (CatalogEdition::Ifc2x3Tc1, CatalogProfile::Official),
SchemaVersion::Ifc4 => (CatalogEdition::Ifc4Add2Tc1, CatalogProfile::Corrected),
SchemaVersion::Ifc4x3 => (CatalogEdition::Ifc4x3Add2, CatalogProfile::Corrected),
_ => return Ok(None),
};
let catalog = load_catalog(edition, profile).map_err(|error| {
PropertyEditFailure::InvalidModel(format!(
"the {edition:?} catalog cannot be read: {error}"
))
})?;
let Some(found) = catalog.get(set) else {
return Ok(None);
};
let mut members = BTreeMap::new();
let (quantity, context) = match &found.kind {
SetTemplateKind::Property {
set_type,
properties,
} => {
for property in properties {
let (form, data_type, values) = match &property.kind {
PropertyKind::SingleValue { data_type } => (
TemplateForm::Single,
data_type.type_name.clone(),
Vec::new(),
),
PropertyKind::EnumeratedValue {
data_type,
values,
constants,
..
} => (
TemplateForm::Enumerated,
data_type.as_ref().and_then(|d| d.type_name.clone()),
values
.iter()
.cloned()
.chain(constants.iter().map(|c| c.name.clone()))
.collect(),
),
PropertyKind::ListValue { data_type } => {
(TemplateForm::List, data_type.type_name.clone(), Vec::new())
}
PropertyKind::BoundedValue { .. } => {
(TemplateForm::Other("a bounded value"), None, Vec::new())
}
PropertyKind::TableValue { .. } => {
(TemplateForm::Other("a table value"), None, Vec::new())
}
PropertyKind::ReferenceValue { .. } => {
(TemplateForm::Other("a reference value"), None, Vec::new())
}
_ => (TemplateForm::Other("a complex property"), None, Vec::new()),
};
members.insert(
property.name.clone(),
MemberTemplate {
form,
data_type,
values,
},
);
}
let context = match set_type {
PropertySetType::TypeDrivenOnly => Context::TypeOnly,
PropertySetType::OccurrenceDriven => Context::OccurrenceOnly,
_ => Context::Any,
};
(false, context)
}
SetTemplateKind::Quantity {
set_type,
quantities,
..
} => {
for quantity in quantities {
let form = match quantity.kind {
Qto::Length => TemplateForm::Quantity(QuantityKind::Length),
Qto::Area => TemplateForm::Quantity(QuantityKind::Area),
Qto::Volume => TemplateForm::Quantity(QuantityKind::Volume),
Qto::Weight => TemplateForm::Quantity(QuantityKind::Weight),
Qto::Time => TemplateForm::Quantity(QuantityKind::Time),
Qto::Count => TemplateForm::Quantity(QuantityKind::Count),
Qto::Number => TemplateForm::Quantity(QuantityKind::Number),
_ => TemplateForm::Other("a quantity kind this writer does not know"),
};
members.insert(
quantity.name.clone(),
MemberTemplate {
form,
data_type: None,
values: Vec::new(),
},
);
}
let context = match set_type {
QuantitySetType::TypeDrivenOnly => Context::TypeOnly,
QuantitySetType::OccurrenceDriven => Context::OccurrenceOnly,
_ => Context::Any,
};
(true, context)
}
_ => return Ok(None),
};
Ok(Some(SetTemplate {
name: found.name.clone(),
quantity,
context,
members,
}))
}