#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SchemaVersion {
Ifc2x3,
Ifc4,
Ifc4x3,
}
impl SchemaVersion {
pub fn from_file_schema(label: &str) -> Option<Self> {
let upper = label.trim().to_ascii_uppercase();
if is_schema_family(&upper, "IFC4X3") {
Some(Self::Ifc4x3)
} else if is_schema_family(&upper, "IFC4") {
Some(Self::Ifc4)
} else if is_schema_family(&upper, "IFC2X3") {
Some(Self::Ifc2x3)
} else {
None
}
}
pub fn attribute_names(self, entity_name: &str) -> Option<&'static [&'static str]> {
match self {
Self::Ifc2x3 => attribute_names_ifc2x3(entity_name),
Self::Ifc4 => attribute_names_ifc4(entity_name),
Self::Ifc4x3 => attribute_names_ifc4x3(entity_name),
}
}
}
fn is_schema_family(label: &str, family: &str) -> bool {
let Some(suffix) = label.strip_prefix(family) else {
return false;
};
if suffix.is_empty() {
return true;
}
let Some(revision) = suffix.strip_prefix('_') else {
return false;
};
revision.split('_').all(|component| {
["ADD", "TC", "RC"].iter().any(|prefix| {
component.strip_prefix(prefix).is_some_and(|number| {
!number.is_empty() && number.bytes().all(|b| b.is_ascii_digit())
})
})
})
}
fn attribute_names_ifc2x3(name: &str) -> Option<&'static [&'static str]> {
let ty = super::ifc2x3::IfcType::from_str(name);
if matches!(ty, super::ifc2x3::IfcType::Unknown(_)) {
None
} else {
Some(ty.attribute_names())
}
}
fn attribute_names_ifc4(name: &str) -> Option<&'static [&'static str]> {
let ty = super::ifc4::IfcType::from_str(name);
if matches!(ty, super::ifc4::IfcType::Unknown(_)) {
None
} else {
Some(ty.attribute_names())
}
}
fn attribute_names_ifc4x3(name: &str) -> Option<&'static [&'static str]> {
let ty = super::schema::IfcType::from_str(name);
if ty.declared_by_canonical_schema() {
Some(ty.attribute_names())
} else {
None
}
}
pub fn attribute_names_for_schema(
file_schema: &str,
entity_name: &str,
) -> Option<&'static [&'static str]> {
SchemaVersion::from_file_schema(file_schema)?.attribute_names(entity_name)
}