Skip to main content

ifc_resource/actor/
person.rs

1//! `IfcPerson` — bounded person projection (IFC2X3, IFC4, IFC4X3).
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`). Under IFC2X3 the
14/// release's own `IfcPerson.WR1` applies instead: `FamilyName` or
15/// `GivenName` must exist, and `Id` does not count.
16#[derive(Debug, Clone, Copy)]
17pub struct Person<'m, 's> {
18    record: Record<'m, 's>,
19}
20
21impl<'m, 's> Person<'m, 's> {
22    pub(crate) fn from_record(record: Record<'m, 's>) -> ResourceResult<Self> {
23        let person = Self { record };
24        let identification = person.identification()?;
25        let family_name = person.record.optional_text("FamilyName")?;
26        let given_name = person.record.optional_text("GivenName")?;
27        if person.record.is_ifc2x3() {
28            if family_name.is_none() && given_name.is_none() {
29                return Err(ResourceError::SemanticViolation {
30                    entity: Some(person.record.id),
31                    rule: "IfcPerson.WR1 (IFC2X3) requires FamilyName or GivenName",
32                });
33            }
34            return Ok(person);
35        }
36        if identification.is_none() && family_name.is_none() && given_name.is_none() {
37            return Err(ResourceError::SemanticViolation {
38                entity: Some(person.record.id),
39                rule:
40                    "IfcPerson.IdentifiablePerson requires Identification, FamilyName, or GivenName",
41            });
42        }
43        let middle_names = person.record.optional_text_list("MiddleNames", 1)?;
44        if !middle_names.is_empty() && family_name.is_none() && given_name.is_none() {
45            return Err(ResourceError::SemanticViolation {
46                entity: Some(person.record.id),
47                rule: "IfcPerson.ValidSetOfNames requires FamilyName or GivenName with MiddleNames",
48            });
49        }
50        Ok(person)
51    }
52
53    /// The entity id of the projected `IfcPerson`.
54    #[must_use]
55    pub fn id(&self) -> EntityId {
56        self.record.id
57    }
58
59    /// The `Identification` attribute, when authored. Under IFC2X3 this
60    /// reads `Id`, the `IfcIdentifier` IFC2X3 declares in the same position.
61    pub fn identification(&self) -> ResourceResult<Option<&'m str>> {
62        if self.record.is_ifc2x3() {
63            self.record.optional_text("Id")
64        } else {
65            self.record.optional_text("Identification")
66        }
67    }
68
69    /// The `FamilyName` attribute, when authored.
70    pub fn family_name(&self) -> ResourceResult<Option<&'m str>> {
71        self.record.optional_text("FamilyName")
72    }
73
74    /// The `GivenName` attribute, when authored.
75    pub fn given_name(&self) -> ResourceResult<Option<&'m str>> {
76        self.record.optional_text("GivenName")
77    }
78
79    /// The `MiddleNames` attribute, when authored.
80    pub fn middle_names(&self) -> ResourceResult<Vec<&'m str>> {
81        self.record.optional_text_list("MiddleNames", 1)
82    }
83
84    /// The `PrefixTitles` attribute, when authored.
85    pub fn prefix_titles(&self) -> ResourceResult<Vec<&'m str>> {
86        self.record.optional_text_list("PrefixTitles", 1)
87    }
88
89    /// The `SuffixTitles` attribute, when authored.
90    pub fn suffix_titles(&self) -> ResourceResult<Vec<&'m str>> {
91        self.record.optional_text_list("SuffixTitles", 1)
92    }
93
94    /// The `Roles` attribute: entity references, when authored.
95    pub fn role_ids(&self) -> ResourceResult<Vec<EntityId>> {
96        self.record.refs("Roles", "IfcActorRole", 1, true, false)
97    }
98
99    /// The `Roles` attribute, resolved to projected `IfcActorRole` values.
100    pub fn roles(&self) -> ResourceResult<Vec<ActorRole<'m, 's>>> {
101        self.role_ids()?
102            .into_iter()
103            .map(|id| {
104                Record::new(self.record.model, self.record.schema, id, "IfcActorRole")
105                    .and_then(ActorRole::from_record)
106            })
107            .collect()
108    }
109
110    /// The `Addresses` attribute, when authored.
111    pub fn address_ids(&self) -> ResourceResult<Vec<EntityId>> {
112        self.record.refs("Addresses", "IfcAddress", 1, true, false)
113    }
114}