Skip to main content

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}