Skip to main content

jellyflow_runtime/runtime/conformance/scenario/
trace.rs

1use serde::{Deserialize, Serialize};
2
3use crate::runtime::events::{
4    ConnectEnd, ConnectStart, NodeDragEnd, NodeDragStart, NodeDragUpdate, NodeGraphGestureEvent,
5    NodeGraphStoreEvent, NodeResizeEnd, NodeResizeStart, NodeResizeUpdate, ViewChange,
6    ViewportMove, ViewportMoveEnd, ViewportMoveStart,
7};
8use crate::runtime::xyflow::callbacks::{ConnectionChange, EdgeConnection};
9use jellyflow_core::core::{CanvasPoint, EdgeId, GroupId, NodeId};
10use jellyflow_core::ops::EdgeEndpoints;
11
12#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13#[serde(tag = "kind", content = "data", rename_all = "snake_case")]
14pub enum ConformanceTraceEvent {
15    DocumentReplaced {
16        before_revision: u64,
17        after_revision: u64,
18    },
19    GraphCommitted {
20        label: Option<String>,
21        op_kinds: Vec<String>,
22    },
23    ViewChanged {
24        changes: Vec<ConformanceViewChange>,
25    },
26    Gesture(NodeGraphGestureEvent),
27    Callback(ConformanceCallbackEvent),
28}
29
30impl ConformanceTraceEvent {
31    pub fn graph_commit(
32        label: Option<impl Into<String>>,
33        op_kinds: impl IntoIterator<Item = impl AsRef<str>>,
34    ) -> Self {
35        Self::GraphCommitted {
36            label: label.map(Into::into),
37            op_kinds: op_kinds
38                .into_iter()
39                .map(|kind| kind.as_ref().to_owned())
40                .collect(),
41        }
42    }
43
44    pub fn viewport(pan: CanvasPoint, zoom: f32) -> Self {
45        Self::ViewChanged {
46            changes: vec![ConformanceViewChange::Viewport { pan, zoom }],
47        }
48    }
49
50    pub fn selection(
51        nodes: impl IntoIterator<Item = NodeId>,
52        edges: impl IntoIterator<Item = EdgeId>,
53        groups: impl IntoIterator<Item = GroupId>,
54    ) -> Self {
55        Self::ViewChanged {
56            changes: vec![ConformanceViewChange::Selection {
57                nodes: nodes.into_iter().collect(),
58                edges: edges.into_iter().collect(),
59                groups: groups.into_iter().collect(),
60            }],
61        }
62    }
63
64    pub fn gesture(event: NodeGraphGestureEvent) -> Self {
65        Self::Gesture(event)
66    }
67
68    pub fn callback(event: ConformanceCallbackEvent) -> Self {
69        Self::Callback(event)
70    }
71
72    pub fn from_store_event(event: NodeGraphStoreEvent<'_>) -> Self {
73        match event {
74            NodeGraphStoreEvent::DocumentReplaced { before, after } => Self::DocumentReplaced {
75                before_revision: before.graph_revision,
76                after_revision: after.graph_revision,
77            },
78            NodeGraphStoreEvent::GraphCommitted { patch } => Self::GraphCommitted {
79                label: patch.transaction().label().map(str::to_owned),
80                op_kinds: patch
81                    .transaction()
82                    .ops()
83                    .iter()
84                    .map(serialized_graph_op_kind)
85                    .collect(),
86            },
87            NodeGraphStoreEvent::ViewChanged { changes, .. } => Self::ViewChanged {
88                changes: changes
89                    .iter()
90                    .map(ConformanceViewChange::from_view_change)
91                    .collect(),
92            },
93        }
94    }
95}
96
97#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
98#[serde(tag = "kind", rename_all = "snake_case")]
99pub enum ConformanceViewChange {
100    Viewport {
101        pan: CanvasPoint,
102        zoom: f32,
103    },
104    Selection {
105        #[serde(default, skip_serializing_if = "Vec::is_empty")]
106        nodes: Vec<NodeId>,
107        #[serde(default, skip_serializing_if = "Vec::is_empty")]
108        edges: Vec<EdgeId>,
109        #[serde(default, skip_serializing_if = "Vec::is_empty")]
110        groups: Vec<GroupId>,
111    },
112}
113
114impl ConformanceViewChange {
115    pub fn from_view_change(change: &ViewChange) -> Self {
116        match change {
117            ViewChange::Viewport { pan, zoom } => Self::Viewport {
118                pan: *pan,
119                zoom: *zoom,
120            },
121            ViewChange::Selection {
122                nodes,
123                edges,
124                groups,
125            } => Self::Selection {
126                nodes: nodes.clone(),
127                edges: edges.clone(),
128                groups: groups.clone(),
129            },
130        }
131    }
132}
133
134#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
135#[serde(tag = "kind", content = "data", rename_all = "snake_case")]
136pub enum ConformanceCallbackEvent {
137    ViewChange {
138        changes: Vec<ConformanceViewChange>,
139    },
140    ViewportChange {
141        pan: CanvasPoint,
142        zoom: f32,
143    },
144    SelectionChange {
145        nodes: Vec<NodeId>,
146        edges: Vec<EdgeId>,
147        groups: Vec<GroupId>,
148    },
149    GraphCommit {
150        label: Option<String>,
151    },
152    NodeEdgeChanges {
153        nodes: usize,
154        edges: usize,
155    },
156    NodesChange {
157        count: usize,
158    },
159    EdgesChange {
160        count: usize,
161    },
162    NodesDelete {
163        count: usize,
164    },
165    EdgesDelete {
166        count: usize,
167    },
168    GroupsDelete {
169        count: usize,
170    },
171    StickyNotesDelete {
172        count: usize,
173    },
174    Delete {
175        nodes: usize,
176        edges: usize,
177        groups: usize,
178        sticky_notes: usize,
179    },
180    ConnectionChange(ConnectionChange),
181    Connect(EdgeConnection),
182    Disconnect(EdgeConnection),
183    Reconnect {
184        edge: EdgeId,
185        from: EdgeEndpoints,
186        to: EdgeEndpoints,
187    },
188    NodeDragStart(NodeDragStart),
189    NodeDrag(NodeDragUpdate),
190    NodeDragEnd(NodeDragEnd),
191    NodeResizeStart(NodeResizeStart),
192    NodeResize(NodeResizeUpdate),
193    NodeResizeEnd(NodeResizeEnd),
194    ViewportMoveStart(ViewportMoveStart),
195    ViewportMove(ViewportMove),
196    ViewportMoveEnd(ViewportMoveEnd),
197    ConnectStart(ConnectStart),
198    ConnectEnd(ConnectEnd),
199}
200
201fn serialized_graph_op_kind(op: &jellyflow_core::ops::GraphOp) -> String {
202    serde_json::to_value(op)
203        .ok()
204        .and_then(|value| {
205            value
206                .get("op")
207                .and_then(|op| op.as_str())
208                .map(str::to_owned)
209        })
210        .unwrap_or_else(|| "unknown".to_owned())
211}