Skip to main content

jellyflow_runtime/runtime/xyflow/apply/
ordered.rs

1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4
5use jellyflow_core::core::{CanvasPoint, CanvasSize, Edge, EdgeId, Node, NodeId};
6
7/// Adapter-owned node array element for exact XyFlow `applyNodeChanges` semantics.
8#[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/// Adapter-owned edge array element for exact XyFlow `applyEdgeChanges` semantics.
42#[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/// XyFlow's `setAttributes` dimension switch.
61#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
62#[serde(rename_all = "snake_case")]
63pub enum XyFlowDimensionAttribute {
64    Width,
65    Height,
66}
67
68/// XyFlow's `boolean | 'width' | 'height'` dimension attribute contract.
69#[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/// XyFlow node changes as applied to adapter-owned ordered node arrays.
93#[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/// XyFlow edge changes as applied to adapter-owned ordered edge arrays.
143#[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}