1use crate::element::{ElementRecord, Placement};
14use crate::id::GlobalId;
15use crate::property::PropertyValue;
16use crate::representation::Representation;
17use glam::DVec3;
18use serde::{Deserialize, Serialize};
19
20#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
22pub enum ModelCommand {
23 CreateElement {
26 element: Box<ElementRecord>,
27 },
28 DeleteElement {
29 global_id: GlobalId,
30 },
31 SetName {
32 global_id: GlobalId,
33 name: Option<String>,
34 },
35 SetProperty {
36 global_id: GlobalId,
37 set: String,
38 name: String,
39 value: Option<PropertyValue>,
41 },
42 MoveElement {
45 global_id: GlobalId,
46 delta: DVec3,
47 },
48 SetPlacement {
49 global_id: GlobalId,
50 placement: Placement,
51 },
52 AssignContainer {
54 global_id: GlobalId,
55 container: Option<GlobalId>,
56 },
57 SetRepresentation {
62 global_id: GlobalId,
63 representation: Option<Box<Representation>>,
64 },
65 AssignType {
67 global_id: GlobalId,
68 type_ref: Option<GlobalId>,
69 },
70 AddVoid {
72 host: GlobalId,
73 opening: GlobalId,
74 },
75 RemoveVoid {
76 host: GlobalId,
77 opening: GlobalId,
78 },
79 AddFill {
81 opening: GlobalId,
82 filler: GlobalId,
83 },
84 RemoveFill {
85 opening: GlobalId,
86 filler: GlobalId,
87 },
88}
89
90impl ModelCommand {
91 pub fn primary_target(&self) -> &GlobalId {
93 match self {
94 Self::CreateElement { element } => &element.global_id,
95 Self::DeleteElement { global_id }
96 | Self::SetName { global_id, .. }
97 | Self::SetProperty { global_id, .. }
98 | Self::MoveElement { global_id, .. }
99 | Self::SetPlacement { global_id, .. }
100 | Self::AssignContainer { global_id, .. }
101 | Self::SetRepresentation { global_id, .. }
102 | Self::AssignType { global_id, .. } => global_id,
103 Self::AddVoid { host, .. } | Self::RemoveVoid { host, .. } => host,
104 Self::AddFill { opening, .. } | Self::RemoveFill { opening, .. } => opening,
105 }
106 }
107
108 pub fn invalidates_geometry(&self) -> bool {
113 match self {
114 Self::CreateElement { .. }
115 | Self::DeleteElement { .. }
116 | Self::MoveElement { .. }
117 | Self::SetPlacement { .. }
118 | Self::AddVoid { .. }
119 | Self::RemoveVoid { .. }
120 | Self::AddFill { .. }
121 | Self::RemoveFill { .. }
122 | Self::SetRepresentation { .. } => true,
123 Self::SetName { .. }
126 | Self::SetProperty { .. }
127 | Self::AssignType { .. }
128 | Self::AssignContainer { .. } => false,
129 }
130 }
131}
132
133#[derive(Debug, Clone, PartialEq)]
135pub struct CommandOutcome {
136 pub revision: u64,
138 pub inverse: ModelCommand,
140 pub changed: Vec<GlobalId>,
142 pub geometry_invalidated: Vec<GlobalId>,
144}
145
146#[derive(Debug, Clone, PartialEq, thiserror::Error)]
151pub enum CommandError {
152 #[error("no element {0}")]
153 UnknownElement(GlobalId),
154
155 #[error("element {0} already exists")]
156 DuplicateElement(GlobalId),
157
158 #[error("{host} is a {class} and cannot host openings")]
159 NotAHost { host: GlobalId, class: String },
160
161 #[error("{0} is not an IfcOpeningElement")]
162 NotAnOpening(GlobalId),
163
164 #[error("an element cannot reference itself ({0})")]
165 SelfReference(GlobalId),
166
167 #[error("{global_id} is still referenced by {relationship}; remove the relationship first")]
168 StillReferenced {
169 global_id: GlobalId,
170 relationship: &'static str,
171 },
172
173 #[error("relationship {relationship} between {a} and {b} does not exist")]
174 NoSuchRelationship {
175 relationship: &'static str,
176 a: GlobalId,
177 b: GlobalId,
178 },
179
180 #[error("relationship {relationship} between {a} and {b} already exists")]
181 RelationshipExists {
182 relationship: &'static str,
183 a: GlobalId,
184 b: GlobalId,
185 },
186
187 #[error("a spatial container must be spatial; {0} is not")]
188 NotSpatial(GlobalId),
189
190 #[error("the representation for {0} is malformed and would produce an unreadable file")]
191 InvalidRepresentation(GlobalId),
192
193 #[error("nothing to undo")]
194 NothingToUndo,
195
196 #[error("nothing to redo")]
197 NothingToRedo,
198}