Skip to main content

ifc_resource/actor/
person.rs

1//! `IfcPerson` — bounded IFC4 person projection.
2
3use ifc_model::EntityId;
4
5use crate::actor::role::ActorRole;
6use crate::error::{ResourceError, ResourceResult};
7use crate::view::Record;
8
9/// A borrowed, schema-resolved `IfcPerson` projection.
10///
11/// Enforces `IfcPerson.IdentifiablePerson` (at least one of `Identification`,
12/// `FamilyName`, `GivenName` is present) and `IfcPerson.ValidSetOfNames`
13/// (`MiddleNames` requires `FamilyName` or `GivenName`).
14#[derive(Debug, Clone, Copy)]
15pub struct Person<'m, 's> {
16    record: Record<'m, 's>,
17}
18
19impl<'m, 's> Person<'m, 's> {
20    pub(crate) fn from_record(record: Record<'m, 's>) -> ResourceResult<Self> {
21        let person = Self { record };
22        let identification = person.record.optional_text("Identification")?;
23        let family_name = person.record.optional_text("FamilyName")?;
24        let given_name = person.record.optional_text("GivenName")?;
25        if identification.is_none() && family_name.is_none() && given_name.is_none() {
26            return Err(ResourceError::SemanticViolation {
27                entity: Some(person.record.id),
28                rule:
29                    "IfcPerson.IdentifiablePerson requires Identification, FamilyName, or GivenName",
30            });
31        }
32        let middle_names = person.record.optional_text_list("MiddleNames", 1)?;
33        if !middle_names.is_empty() && family_name.is_none() && given_name.is_none() {
34            return Err(ResourceError::SemanticViolation {
35                entity: Some(person.record.id),
36                rule: "IfcPerson.ValidSetOfNames requires FamilyName or GivenName with MiddleNames",
37            });
38        }
39        Ok(person)
40    }
41
42    /// The entity id of the projected `IfcPerson`.
43    #[must_use]
44    pub fn id(&self) -> EntityId {
45        self.record.id
46    }
47
48    /// The `Identification` attribute, when authored.
49    pub fn identification(&self) -> ResourceResult<Option<&'m str>> {
50        self.record.optional_text("Identification")
51    }
52
53    /// The `FamilyName` attribute, when authored.
54    pub fn family_name(&self) -> ResourceResult<Option<&'m str>> {
55        self.record.optional_text("FamilyName")
56    }
57
58    /// The `GivenName` attribute, when authored.
59    pub fn given_name(&self) -> ResourceResult<Option<&'m str>> {
60        self.record.optional_text("GivenName")
61    }
62
63    /// The `MiddleNames` attribute, when authored.
64    pub fn middle_names(&self) -> ResourceResult<Vec<&'m str>> {
65        self.record.optional_text_list("MiddleNames", 1)
66    }
67
68    /// The `PrefixTitles` attribute, when authored.
69    pub fn prefix_titles(&self) -> ResourceResult<Vec<&'m str>> {
70        self.record.optional_text_list("PrefixTitles", 1)
71    }
72
73    /// The `SuffixTitles` attribute, when authored.
74    pub fn suffix_titles(&self) -> ResourceResult<Vec<&'m str>> {
75        self.record.optional_text_list("SuffixTitles", 1)
76    }
77
78    /// The `Roles` attribute: entity references, when authored.
79    pub fn role_ids(&self) -> ResourceResult<Vec<EntityId>> {
80        self.record.refs("Roles", "IfcActorRole", 1, true, false)
81    }
82
83    /// The `Roles` attribute, resolved to projected `IfcActorRole` values.
84    pub fn roles(&self) -> ResourceResult<Vec<ActorRole<'m, 's>>> {
85        self.role_ids()?
86            .into_iter()
87            .map(|id| {
88                Record::new(self.record.model, self.record.schema, id, "IfcActorRole")
89                    .and_then(ActorRole::from_record)
90            })
91            .collect()
92    }
93
94    /// The `Addresses` attribute, when authored.
95    pub fn address_ids(&self) -> ResourceResult<Vec<EntityId>> {
96        self.record.refs("Addresses", "IfcAddress", 1, true, false)
97    }
98}