1use std::collections::BTreeSet;
2
3use serde::{Deserialize, Serialize};
4
5use crate::core::{
6 Binding, BindingEndpoint, BindingId, Edge, EdgeId, GraphId, GraphLocalBindingTarget, GroupId,
7 Node, NodeId, Port, PortId, StickyNoteId, SymbolId,
8};
9
10use super::{batch::GraphTransaction, endpoints::EdgeEndpoints, op::GraphOp};
11
12fn is_false(value: &bool) -> bool {
13 !*value
14}
15
16#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
21pub struct GraphMutationFootprint {
22 #[serde(default, skip_serializing_if = "is_false")]
24 pub graph: bool,
25 #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
27 pub imports: BTreeSet<GraphId>,
28 #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
30 pub symbols: BTreeSet<SymbolId>,
31 #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
33 pub nodes: BTreeSet<NodeId>,
34 #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
36 pub ports: BTreeSet<PortId>,
37 #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
39 pub edges: BTreeSet<EdgeId>,
40 #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
42 pub groups: BTreeSet<GroupId>,
43 #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
45 pub sticky_notes: BTreeSet<StickyNoteId>,
46 #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
48 pub bindings: BTreeSet<BindingId>,
49}
50
51impl GraphMutationFootprint {
52 pub fn new() -> Self {
54 Self::default()
55 }
56
57 pub fn is_empty(&self) -> bool {
59 !self.graph
60 && self.imports.is_empty()
61 && self.symbols.is_empty()
62 && self.nodes.is_empty()
63 && self.ports.is_empty()
64 && self.edges.is_empty()
65 && self.groups.is_empty()
66 && self.sticky_notes.is_empty()
67 && self.bindings.is_empty()
68 }
69
70 pub fn extend(&mut self, other: Self) {
72 self.graph |= other.graph;
73 self.imports.extend(other.imports);
74 self.symbols.extend(other.symbols);
75 self.nodes.extend(other.nodes);
76 self.ports.extend(other.ports);
77 self.edges.extend(other.edges);
78 self.groups.extend(other.groups);
79 self.sticky_notes.extend(other.sticky_notes);
80 self.bindings.extend(other.bindings);
81 }
82
83 pub fn touch_graph(&mut self) {
85 self.graph = true;
86 }
87
88 pub fn touch_node(&mut self, id: NodeId) {
90 self.nodes.insert(id);
91 }
92
93 pub fn touch_port(&mut self, id: PortId) {
95 self.ports.insert(id);
96 }
97
98 pub fn touch_edge(&mut self, id: EdgeId) {
100 self.edges.insert(id);
101 }
102
103 pub fn touch_group(&mut self, id: GroupId) {
105 self.groups.insert(id);
106 }
107
108 pub fn touch_sticky_note(&mut self, id: StickyNoteId) {
110 self.sticky_notes.insert(id);
111 }
112
113 pub fn touch_binding(&mut self, id: BindingId) {
115 self.bindings.insert(id);
116 }
117
118 pub fn touch_import(&mut self, id: GraphId) {
120 self.imports.insert(id);
121 }
122
123 pub fn touch_symbol(&mut self, id: SymbolId) {
125 self.symbols.insert(id);
126 }
127
128 pub fn touch_node_snapshot(&mut self, id: NodeId, node: &Node) {
130 self.touch_node(id);
131 self.ports.extend(node.ports.iter().copied());
132 if let Some(parent) = node.parent {
133 self.touch_group(parent);
134 }
135 }
136
137 pub fn touch_port_snapshot(&mut self, id: PortId, port: &Port) {
139 self.touch_port(id);
140 self.touch_node(port.node);
141 }
142
143 pub fn touch_edge_snapshot(&mut self, id: EdgeId, edge: &Edge) {
145 self.touch_edge(id);
146 self.touch_edge_endpoints(EdgeEndpoints::from_edge(edge));
147 }
148
149 pub fn touch_edge_endpoints(&mut self, endpoints: EdgeEndpoints) {
151 self.touch_port(endpoints.from);
152 self.touch_port(endpoints.to);
153 }
154
155 pub fn touch_binding_snapshot(&mut self, id: BindingId, binding: &Binding) {
157 self.touch_binding(id);
158 self.touch_binding_endpoint(&binding.subject);
159 self.touch_binding_endpoint(&binding.target);
160 }
161
162 pub fn touch_binding_endpoint(&mut self, endpoint: &BindingEndpoint) {
164 let Some(target) = endpoint.graph_local_target() else {
165 return;
166 };
167
168 match target {
169 GraphLocalBindingTarget::Graph => self.touch_graph(),
170 GraphLocalBindingTarget::Node { id } => self.touch_node(id),
171 GraphLocalBindingTarget::Port { id } => self.touch_port(id),
172 GraphLocalBindingTarget::Edge { id } => self.touch_edge(id),
173 GraphLocalBindingTarget::Group { id } => self.touch_group(id),
174 GraphLocalBindingTarget::StickyNote { id } => self.touch_sticky_note(id),
175 }
176 }
177}
178
179impl GraphOp {
180 pub fn footprint(&self) -> GraphMutationFootprint {
182 let mut footprint = GraphMutationFootprint::new();
183 self.append_footprint(&mut footprint);
184 footprint
185 }
186
187 pub fn append_footprint(&self, footprint: &mut GraphMutationFootprint) {
189 match self {
190 Self::AddNode { id, node } | Self::RemoveNode { id, node, .. } => {
191 footprint.touch_node_snapshot(*id, node);
192 }
193 Self::SetNodePos { id, .. }
194 | Self::SetNodeOrigin { id, .. }
195 | Self::SetNodeKind { id, .. }
196 | Self::SetNodeKindVersion { id, .. }
197 | Self::SetNodeSelectable { id, .. }
198 | Self::SetNodeFocusable { id, .. }
199 | Self::SetNodeDraggable { id, .. }
200 | Self::SetNodeConnectable { id, .. }
201 | Self::SetNodeDeletable { id, .. }
202 | Self::SetNodeExtent { id, .. }
203 | Self::SetNodeExpandParent { id, .. }
204 | Self::SetNodeSize { id, .. }
205 | Self::SetNodeHidden { id, .. }
206 | Self::SetNodeCollapsed { id, .. }
207 | Self::SetNodeData { id, .. } => {
208 footprint.touch_node(*id);
209 }
210 Self::SetNodeParent { id, from, to } => {
211 footprint.touch_node(*id);
212 if let Some(parent) = from {
213 footprint.touch_group(*parent);
214 }
215 if let Some(parent) = to {
216 footprint.touch_group(*parent);
217 }
218 }
219 Self::SetNodePorts { id, from, to } => {
220 footprint.touch_node(*id);
221 footprint.ports.extend(from.iter().copied());
222 footprint.ports.extend(to.iter().copied());
223 }
224
225 Self::AddPort { id, port } | Self::RemovePort { id, port, .. } => {
226 footprint.touch_port_snapshot(*id, port);
227 }
228 Self::SetPortConnectable { id, .. }
229 | Self::SetPortConnectableStart { id, .. }
230 | Self::SetPortConnectableEnd { id, .. }
231 | Self::SetPortType { id, .. }
232 | Self::SetPortData { id, .. } => {
233 footprint.touch_port(*id);
234 }
235
236 Self::AddEdge { id, edge } | Self::RemoveEdge { id, edge, .. } => {
237 footprint.touch_edge_snapshot(*id, edge);
238 }
239 Self::SetEdgeKind { id, .. }
240 | Self::SetEdgeSelectable { id, .. }
241 | Self::SetEdgeFocusable { id, .. }
242 | Self::SetEdgeHidden { id, .. }
243 | Self::SetEdgeInteractionWidth { id, .. }
244 | Self::SetEdgeDeletable { id, .. }
245 | Self::SetEdgeReconnectable { id, .. }
246 | Self::SetEdgeData { id, .. }
247 | Self::SetEdgeView { id, .. } => {
248 footprint.touch_edge(*id);
249 }
250 Self::SetEdgeEndpoints { id, from, to } => {
251 footprint.touch_edge(*id);
252 footprint.touch_edge_endpoints(*from);
253 footprint.touch_edge_endpoints(*to);
254 }
255
256 Self::AddImport { id, .. }
257 | Self::RemoveImport { id, .. }
258 | Self::SetImportAlias { id, .. } => {
259 footprint.touch_import(*id);
260 }
261
262 Self::AddSymbol { id, .. }
263 | Self::RemoveSymbol { id, .. }
264 | Self::SetSymbolName { id, .. }
265 | Self::SetSymbolType { id, .. }
266 | Self::SetSymbolDefaultValue { id, .. }
267 | Self::SetSymbolMeta { id, .. } => {
268 footprint.touch_symbol(*id);
269 }
270
271 Self::AddGroup { id, .. } | Self::SetGroupRect { id, .. } => {
272 footprint.touch_group(*id);
273 }
274 Self::RemoveGroup {
275 id,
276 detached,
277 bindings,
278 ..
279 } => {
280 footprint.touch_group(*id);
281 for (node, previous_parent) in detached {
282 footprint.touch_node(*node);
283 if let Some(previous_parent) = previous_parent {
284 footprint.touch_group(*previous_parent);
285 }
286 }
287 for (binding_id, binding) in bindings {
288 footprint.touch_binding_snapshot(*binding_id, binding);
289 }
290 }
291 Self::SetGroupTitle { id, .. } | Self::SetGroupColor { id, .. } => {
292 footprint.touch_group(*id);
293 }
294
295 Self::AddStickyNote { id, .. }
296 | Self::SetStickyNoteText { id, .. }
297 | Self::SetStickyNoteRect { id, .. }
298 | Self::SetStickyNoteColor { id, .. } => {
299 footprint.touch_sticky_note(*id);
300 }
301 Self::RemoveStickyNote { id, bindings, .. } => {
302 footprint.touch_sticky_note(*id);
303 for (binding_id, binding) in bindings {
304 footprint.touch_binding_snapshot(*binding_id, binding);
305 }
306 }
307
308 Self::AddBinding { id, binding } | Self::RemoveBinding { id, binding } => {
309 footprint.touch_binding_snapshot(*id, binding);
310 }
311 Self::SetBindingSubject { id, from, to } | Self::SetBindingTarget { id, from, to } => {
312 footprint.touch_binding(*id);
313 footprint.touch_binding_endpoint(from);
314 footprint.touch_binding_endpoint(to);
315 }
316 Self::SetBindingKind { id, .. } | Self::SetBindingMeta { id, .. } => {
317 footprint.touch_binding(*id);
318 }
319 }
320
321 self.append_cascaded_footprint(footprint);
322 }
323
324 fn append_cascaded_footprint(&self, footprint: &mut GraphMutationFootprint) {
325 match self {
326 Self::RemoveNode {
327 ports,
328 edges,
329 bindings,
330 ..
331 } => {
332 for (port_id, port) in ports {
333 footprint.touch_port_snapshot(*port_id, port);
334 }
335 for (edge_id, edge) in edges {
336 footprint.touch_edge_snapshot(*edge_id, edge);
337 }
338 for (binding_id, binding) in bindings {
339 footprint.touch_binding_snapshot(*binding_id, binding);
340 }
341 }
342 Self::RemovePort {
343 edges, bindings, ..
344 } => {
345 for (edge_id, edge) in edges {
346 footprint.touch_edge_snapshot(*edge_id, edge);
347 }
348 for (binding_id, binding) in bindings {
349 footprint.touch_binding_snapshot(*binding_id, binding);
350 }
351 }
352 Self::RemoveEdge { bindings, .. } => {
353 for (binding_id, binding) in bindings {
354 footprint.touch_binding_snapshot(*binding_id, binding);
355 }
356 }
357 _ => {}
358 }
359 }
360}
361
362impl GraphTransaction {
363 pub fn footprint(&self) -> GraphMutationFootprint {
365 let mut footprint = GraphMutationFootprint::new();
366 self.append_footprint(&mut footprint);
367 footprint
368 }
369
370 pub fn append_footprint(&self, footprint: &mut GraphMutationFootprint) {
372 for op in self.ops() {
373 op.append_footprint(footprint);
374 }
375 }
376}