ifc_spatial/tree/kind.rs
1//! Where an entity sits in the spatial hierarchy.
2
3/// The spatial role of an entity, as far as containment is concerned.
4///
5/// This is a *structural* classification, not a schema subtype check: it asks
6/// "can this contain things, and roughly at what level", which is what tree
7/// assembly needs. A full subtype test needs `ifc-schema` and belongs in a
8/// validation pass.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
10pub enum SpatialKind {
11 /// `IfcProject` -- the root. A conformant file has exactly one.
12 Project,
13 /// `IfcSite`.
14 Site,
15 /// `IfcBuilding`.
16 Building,
17 /// `IfcBuildingStorey`.
18 Storey,
19 /// `IfcSpace`.
20 Space,
21 /// Any other spatial container, including IFC4x3 facility types such as
22 /// `IfcBridge` and `IfcRoad`, which are recognised by suffix so this crate
23 /// does not need a per-version type list.
24 OtherContainer,
25 /// A physical element: wall, door, slab. Contained, never a container.
26 Element,
27}
28
29impl SpatialKind {
30 /// Classify a STEP type name.
31 ///
32 /// Unknown types are [`Element`](Self::Element): an entity that no
33 /// relationship names as a container is a leaf, and treating it as one
34 /// keeps unfamiliar IFC4x3 or vendor types in the tree rather than
35 /// dropping them.
36 #[must_use]
37 pub fn classify(type_name: &str) -> Self {
38 let upper = type_name.to_ascii_uppercase();
39 match upper.as_str() {
40 "IFCPROJECT" => Self::Project,
41 "IFCSITE" => Self::Site,
42 "IFCBUILDING" => Self::Building,
43 "IFCBUILDINGSTOREY" => Self::Storey,
44 "IFCSPACE" => Self::Space,
45 // IFC4x3 added facilities (IfcBridge, IfcRoad, IfcRailway) and
46 // their parts. Matching the spatial-element suffix keeps them
47 // classified without pinning this crate to one schema version.
48 _ if upper.starts_with("IFCSPATIAL") || upper == "IFCFACILITY" => Self::OtherContainer,
49 _ => Self::Element,
50 }
51 }
52
53 /// Whether entities of this kind can contain others.
54 #[must_use]
55 pub const fn is_container(self) -> bool {
56 !matches!(self, Self::Element)
57 }
58}