Skip to main content

ifc_resource/actor/
role.rs

1//! `IfcActorRole` — bounded IFC4 role projection.
2
3use ifc_model::EntityId;
4
5use crate::error::{ResourceError, ResourceResult};
6use crate::view::Record;
7
8/// A borrowed, schema-resolved `IfcActorRole` projection.
9///
10/// Enforces `IfcActorRole.WR1`: a `USERDEFINED` role must carry
11/// `UserDefinedRole`.
12#[derive(Debug, Clone, Copy)]
13pub struct ActorRole<'m, 's> {
14    record: Record<'m, 's>,
15}
16
17impl<'m, 's> ActorRole<'m, 's> {
18    pub(crate) fn from_record(record: Record<'m, 's>) -> ResourceResult<Self> {
19        let role = Self { record };
20        role.role()?;
21        Ok(role)
22    }
23
24    /// The entity id of the projected `IfcActorRole`.
25    #[must_use]
26    pub fn id(&self) -> EntityId {
27        self.record.id
28    }
29
30    /// The `Role` enumeration.
31    pub fn role(&self) -> ResourceResult<&'m str> {
32        let value = self.record.required_enum("Role")?;
33        if value.eq_ignore_ascii_case("USERDEFINED")
34            && self
35                .record
36                .optional_text("UserDefinedRole")?
37                .is_none_or(|value| value.trim().is_empty())
38        {
39            return Err(ResourceError::SemanticViolation {
40                entity: Some(self.record.id),
41                rule: "IfcActorRole.WR1 requires UserDefinedRole for USERDEFINED",
42            });
43        }
44        Ok(value)
45    }
46
47    /// The `UserDefinedRole` attribute, when authored.
48    pub fn user_defined_role(&self) -> ResourceResult<Option<&'m str>> {
49        self.record.optional_text("UserDefinedRole")
50    }
51
52    /// The `Description` attribute, when authored.
53    pub fn description(&self) -> ResourceResult<Option<&'m str>> {
54        self.record.optional_text("Description")
55    }
56}