1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4
5use jellyflow_core::core::{CanvasPoint, CanvasSize, Edge, EdgeId, Node, NodeId};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct XyFlowNodeElement {
10 pub id: NodeId,
11 pub node: Node,
12 #[serde(default, skip_serializing_if = "Option::is_none")]
13 pub selected: Option<bool>,
14 #[serde(default, skip_serializing_if = "Option::is_none")]
15 pub dragging: Option<bool>,
16 #[serde(default, skip_serializing_if = "Option::is_none")]
17 pub resizing: Option<bool>,
18 #[serde(default, skip_serializing_if = "Option::is_none")]
19 pub measured: Option<CanvasSize>,
20 #[serde(default, skip_serializing_if = "Option::is_none")]
21 pub width: Option<f32>,
22 #[serde(default, skip_serializing_if = "Option::is_none")]
23 pub height: Option<f32>,
24}
25
26impl XyFlowNodeElement {
27 pub fn new(id: NodeId, node: Node) -> Self {
28 Self {
29 id,
30 node,
31 selected: None,
32 dragging: None,
33 resizing: None,
34 measured: None,
35 width: None,
36 height: None,
37 }
38 }
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct XyFlowEdgeElement {
44 pub id: EdgeId,
45 pub edge: Edge,
46 #[serde(default, skip_serializing_if = "Option::is_none")]
47 pub selected: Option<bool>,
48}
49
50impl XyFlowEdgeElement {
51 pub fn new(id: EdgeId, edge: Edge) -> Self {
52 Self {
53 id,
54 edge,
55 selected: None,
56 }
57 }
58}
59
60#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
62#[serde(rename_all = "snake_case")]
63pub enum XyFlowDimensionAttribute {
64 Width,
65 Height,
66}
67
68#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
70#[serde(untagged)]
71pub enum XyFlowDimensionsSetAttributes {
72 Bool(bool),
73 Attribute(XyFlowDimensionAttribute),
74}
75
76impl XyFlowDimensionsSetAttributes {
77 pub fn writes_width(self) -> bool {
78 matches!(
79 self,
80 Self::Bool(true) | Self::Attribute(XyFlowDimensionAttribute::Width)
81 )
82 }
83
84 pub fn writes_height(self) -> bool {
85 matches!(
86 self,
87 Self::Bool(true) | Self::Attribute(XyFlowDimensionAttribute::Height)
88 )
89 }
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
94#[serde(tag = "type", rename_all = "snake_case")]
95pub enum XyFlowNodeChange {
96 Dimensions {
97 id: NodeId,
98 #[serde(default, skip_serializing_if = "Option::is_none")]
99 dimensions: Option<CanvasSize>,
100 #[serde(default, skip_serializing_if = "Option::is_none")]
101 resizing: Option<bool>,
102 #[serde(
103 default,
104 rename = "setAttributes",
105 alias = "set_attributes",
106 skip_serializing_if = "Option::is_none"
107 )]
108 set_attributes: Option<XyFlowDimensionsSetAttributes>,
109 },
110 Position {
111 id: NodeId,
112 #[serde(default, skip_serializing_if = "Option::is_none")]
113 position: Option<CanvasPoint>,
114 #[serde(
115 default,
116 rename = "positionAbsolute",
117 alias = "position_absolute",
118 skip_serializing_if = "Option::is_none"
119 )]
120 position_absolute: Option<CanvasPoint>,
121 #[serde(default, skip_serializing_if = "Option::is_none")]
122 dragging: Option<bool>,
123 },
124 Select {
125 id: NodeId,
126 selected: bool,
127 },
128 Remove {
129 id: NodeId,
130 },
131 Add {
132 item: XyFlowNodeElement,
133 #[serde(default, skip_serializing_if = "Option::is_none")]
134 index: Option<usize>,
135 },
136 Replace {
137 id: NodeId,
138 item: XyFlowNodeElement,
139 },
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
144#[serde(tag = "type", rename_all = "snake_case")]
145pub enum XyFlowEdgeChange {
146 Select {
147 id: EdgeId,
148 selected: bool,
149 },
150 Remove {
151 id: EdgeId,
152 },
153 Add {
154 item: XyFlowEdgeElement,
155 #[serde(default, skip_serializing_if = "Option::is_none")]
156 index: Option<usize>,
157 },
158 Replace {
159 id: EdgeId,
160 item: XyFlowEdgeElement,
161 },
162}
163
164pub fn apply_xyflow_node_changes(
165 changes: &[XyFlowNodeChange],
166 nodes: &[XyFlowNodeElement],
167) -> Vec<XyFlowNodeElement> {
168 let mut planner = XyFlowNodeApplyPlanner::new(changes, nodes);
169 planner.apply()
170}
171
172pub fn apply_xyflow_edge_changes(
173 changes: &[XyFlowEdgeChange],
174 edges: &[XyFlowEdgeElement],
175) -> Vec<XyFlowEdgeElement> {
176 let mut planner = XyFlowEdgeApplyPlanner::new(changes, edges);
177 planner.apply()
178}
179
180struct XyFlowNodeApplyPlanner<'a> {
181 changes: &'a [XyFlowNodeChange],
182 nodes: &'a [XyFlowNodeElement],
183 changes_by_id: BTreeMap<NodeId, Vec<&'a XyFlowNodeChange>>,
184 add_changes: Vec<&'a XyFlowNodeChange>,
185}
186
187impl<'a> XyFlowNodeApplyPlanner<'a> {
188 fn new(changes: &'a [XyFlowNodeChange], nodes: &'a [XyFlowNodeElement]) -> Self {
189 Self {
190 changes,
191 nodes,
192 changes_by_id: BTreeMap::new(),
193 add_changes: Vec::new(),
194 }
195 }
196
197 fn apply(&mut self) -> Vec<XyFlowNodeElement> {
198 self.index_changes();
199 let mut updated = self.apply_existing_nodes();
200 self.apply_adds(&mut updated);
201 updated
202 }
203
204 fn index_changes(&mut self) {
205 for change in self.changes {
206 match change {
207 XyFlowNodeChange::Add { .. } => self.add_changes.push(change),
208 XyFlowNodeChange::Remove { id } | XyFlowNodeChange::Replace { id, .. } => {
209 self.changes_by_id.insert(*id, vec![change]);
210 }
211 XyFlowNodeChange::Dimensions { id, .. }
212 | XyFlowNodeChange::Position { id, .. }
213 | XyFlowNodeChange::Select { id, .. } => {
214 self.changes_by_id.entry(*id).or_default().push(change);
215 }
216 }
217 }
218 }
219
220 fn apply_existing_nodes(&self) -> Vec<XyFlowNodeElement> {
221 let mut updated = Vec::with_capacity(self.nodes.len() + self.add_changes.len());
222
223 for node in self.nodes {
224 let Some(changes) = self.changes_by_id.get(&node.id) else {
225 updated.push(node.clone());
226 continue;
227 };
228
229 match changes.first() {
230 Some(XyFlowNodeChange::Remove { .. }) => continue,
231 Some(XyFlowNodeChange::Replace { item, .. }) => {
232 updated.push(item.clone());
233 continue;
234 }
235 _ => {}
236 }
237
238 let mut node = node.clone();
239 for change in changes {
240 apply_xyflow_node_change(change, &mut node);
241 }
242 updated.push(node);
243 }
244
245 updated
246 }
247
248 fn apply_adds(&self, updated: &mut Vec<XyFlowNodeElement>) {
249 for change in &self.add_changes {
250 let XyFlowNodeChange::Add { item, index } = change else {
251 continue;
252 };
253 if let Some(index) = index {
254 updated.insert((*index).min(updated.len()), item.clone());
255 } else {
256 updated.push(item.clone());
257 }
258 }
259 }
260}
261
262fn apply_xyflow_node_change(change: &XyFlowNodeChange, node: &mut XyFlowNodeElement) {
263 match change {
264 XyFlowNodeChange::Dimensions {
265 dimensions,
266 resizing,
267 set_attributes,
268 ..
269 } => {
270 if let Some(dimensions) = dimensions {
271 node.measured = Some(*dimensions);
272 if let Some(set_attributes) = set_attributes {
273 if set_attributes.writes_width() {
274 node.width = Some(dimensions.width);
275 }
276 if set_attributes.writes_height() {
277 node.height = Some(dimensions.height);
278 }
279 }
280 }
281 if let Some(resizing) = resizing {
282 node.resizing = Some(*resizing);
283 }
284 }
285 XyFlowNodeChange::Position {
286 position, dragging, ..
287 } => {
288 if let Some(position) = position {
289 node.node.pos = *position;
290 }
291 if let Some(dragging) = dragging {
292 node.dragging = Some(*dragging);
293 }
294 }
295 XyFlowNodeChange::Select { selected, .. } => {
296 node.selected = Some(*selected);
297 }
298 XyFlowNodeChange::Remove { .. }
299 | XyFlowNodeChange::Add { .. }
300 | XyFlowNodeChange::Replace { .. } => {}
301 }
302}
303
304struct XyFlowEdgeApplyPlanner<'a> {
305 changes: &'a [XyFlowEdgeChange],
306 edges: &'a [XyFlowEdgeElement],
307 changes_by_id: BTreeMap<EdgeId, Vec<&'a XyFlowEdgeChange>>,
308 add_changes: Vec<&'a XyFlowEdgeChange>,
309}
310
311impl<'a> XyFlowEdgeApplyPlanner<'a> {
312 fn new(changes: &'a [XyFlowEdgeChange], edges: &'a [XyFlowEdgeElement]) -> Self {
313 Self {
314 changes,
315 edges,
316 changes_by_id: BTreeMap::new(),
317 add_changes: Vec::new(),
318 }
319 }
320
321 fn apply(&mut self) -> Vec<XyFlowEdgeElement> {
322 self.index_changes();
323 let mut updated = self.apply_existing_edges();
324 self.apply_adds(&mut updated);
325 updated
326 }
327
328 fn index_changes(&mut self) {
329 for change in self.changes {
330 match change {
331 XyFlowEdgeChange::Add { .. } => self.add_changes.push(change),
332 XyFlowEdgeChange::Remove { id } | XyFlowEdgeChange::Replace { id, .. } => {
333 self.changes_by_id.insert(*id, vec![change]);
334 }
335 XyFlowEdgeChange::Select { id, .. } => {
336 self.changes_by_id.entry(*id).or_default().push(change);
337 }
338 }
339 }
340 }
341
342 fn apply_existing_edges(&self) -> Vec<XyFlowEdgeElement> {
343 let mut updated = Vec::with_capacity(self.edges.len() + self.add_changes.len());
344
345 for edge in self.edges {
346 let Some(changes) = self.changes_by_id.get(&edge.id) else {
347 updated.push(edge.clone());
348 continue;
349 };
350
351 match changes.first() {
352 Some(XyFlowEdgeChange::Remove { .. }) => continue,
353 Some(XyFlowEdgeChange::Replace { item, .. }) => {
354 updated.push(item.clone());
355 continue;
356 }
357 _ => {}
358 }
359
360 let mut edge = edge.clone();
361 for change in changes {
362 apply_xyflow_edge_change(change, &mut edge);
363 }
364 updated.push(edge);
365 }
366
367 updated
368 }
369
370 fn apply_adds(&self, updated: &mut Vec<XyFlowEdgeElement>) {
371 for change in &self.add_changes {
372 let XyFlowEdgeChange::Add { item, index } = change else {
373 continue;
374 };
375 if let Some(index) = index {
376 updated.insert((*index).min(updated.len()), item.clone());
377 } else {
378 updated.push(item.clone());
379 }
380 }
381 }
382}
383
384fn apply_xyflow_edge_change(change: &XyFlowEdgeChange, edge: &mut XyFlowEdgeElement) {
385 match change {
386 XyFlowEdgeChange::Select { selected, .. } => {
387 edge.selected = Some(*selected);
388 }
389 XyFlowEdgeChange::Remove { .. }
390 | XyFlowEdgeChange::Add { .. }
391 | XyFlowEdgeChange::Replace { .. } => {}
392 }
393}