jellyflow_runtime/runtime/conformance/scenario/
trace.rs1use 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}