ifc_author/editor/
entity.rs1use ifc_model::{Entity, EntityId, Model, Transaction, Value};
8use ifc_schema::Schema;
9
10use crate::builder::check_value;
11use crate::error::{AuthorError, AuthorResult};
12
13#[derive(Debug, Clone)]
20pub struct EntityEditor<'a> {
21 schema: &'a Schema,
22 id: EntityId,
23 entity: Entity,
24 set: Vec<(String, Value)>,
25}
26
27impl<'a> EntityEditor<'a> {
28 pub fn new(schema: &'a Schema, model: &Model, id: EntityId) -> AuthorResult<Self> {
35 let entity = model
36 .get(id)
37 .cloned()
38 .ok_or(AuthorError::MissingEntity { id })?;
39 Ok(Self {
40 schema,
41 id,
42 entity,
43 set: Vec::new(),
44 })
45 }
46
47 #[must_use]
49 pub fn set(mut self, attribute: impl Into<String>, value: Value) -> Self {
50 self.set.push((attribute.into(), value));
51 self
52 }
53
54 #[must_use]
56 pub fn text(self, attribute: impl Into<String>, text: impl Into<std::sync::Arc<str>>) -> Self {
57 self.set(attribute, Value::Text(text.into()))
58 }
59
60 #[must_use]
62 pub fn real(self, attribute: impl Into<String>, value: f64) -> Self {
63 self.set(attribute, Value::Real(value))
64 }
65
66 #[must_use]
68 pub fn reference(self, attribute: impl Into<String>, id: EntityId) -> Self {
69 self.set(attribute, Value::Ref(id))
70 }
71
72 #[must_use]
74 pub fn enumeration(
75 self,
76 attribute: impl Into<String>,
77 constant: impl Into<std::sync::Arc<str>>,
78 ) -> Self {
79 self.set(attribute, Value::Enum(constant.into()))
80 }
81
82 pub fn stage(self, transaction: &mut Transaction) -> AuthorResult<()> {
87 let entity_name = self.entity.type_name.to_string();
88 let declared = self.schema.attributes(&entity_name);
89 if declared.is_empty() && self.schema.entity(&entity_name).is_none() {
90 return Err(AuthorError::UnknownEntity {
91 entity: entity_name,
92 });
93 }
94 if self.entity.attributes.len() != declared.len() {
95 return Err(AuthorError::ArityMismatch {
96 entity: entity_name,
97 expected: declared.len(),
98 found: self.entity.attributes.len(),
99 });
100 }
101
102 let mut projected = self.entity.attributes.clone();
103 let mut edited = vec![false; declared.len()];
104 let mut staged = Vec::with_capacity(self.set.len());
105 for (name, value) in self.set {
106 let Some(index) = declared
107 .iter()
108 .position(|attribute| attribute.name.eq_ignore_ascii_case(&name))
109 else {
110 return Err(AuthorError::UnknownAttribute {
111 entity: entity_name,
112 attribute: name,
113 known: declared
114 .iter()
115 .map(|attribute| attribute.name.clone())
116 .collect(),
117 });
118 };
119 if edited[index] {
120 return Err(AuthorError::DuplicateAttribute {
121 entity: entity_name,
122 attribute: declared[index].name.clone(),
123 });
124 }
125 edited[index] = true;
126 projected[index] = value.clone();
127 staged.push((index, value));
128 }
129
130 for (index, attribute) in declared.iter().enumerate() {
131 let value = &projected[index];
132 check_value(self.schema, &entity_name, attribute, value)?;
135 if !attribute.optional && matches!(value, Value::Null) {
136 return Err(AuthorError::MissingRequired {
137 entity: entity_name,
138 attribute: attribute.name.clone(),
139 });
140 }
141 }
142
143 for (slot, value) in staged {
144 if self.entity.attributes[slot] != value {
145 transaction.set_attribute(self.id, slot, value);
146 }
147 }
148 Ok(())
149 }
150}