jellyflow_runtime/schema/
types.rs1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use jellyflow_core::core::{
5 CanvasPoint, CanvasSize, Node, NodeId, NodeKindKey, Port, PortCapacity, PortDirection, PortId,
6 PortKey, PortKind,
7};
8use jellyflow_core::ops::{GraphOp, GraphTransaction};
9use jellyflow_core::types::TypeDesc;
10
11#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13pub struct PortDecl {
14 pub key: PortKey,
16 pub dir: PortDirection,
18 pub kind: PortKind,
20 pub capacity: PortCapacity,
22 #[serde(default, skip_serializing_if = "Option::is_none")]
24 pub ty: Option<TypeDesc>,
25 #[serde(default, skip_serializing_if = "Option::is_none")]
27 pub label: Option<String>,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct NodeSchema {
33 pub kind: NodeKindKey,
35 pub latest_kind_version: u32,
37 #[serde(default, skip_serializing_if = "Vec::is_empty")]
39 pub kind_aliases: Vec<NodeKindKey>,
40
41 pub title: String,
43 #[serde(default, skip_serializing_if = "Vec::is_empty")]
45 pub category: Vec<String>,
46 #[serde(default, skip_serializing_if = "Vec::is_empty")]
48 pub keywords: Vec<String>,
49 #[serde(default, skip_serializing_if = "Option::is_none")]
54 pub renderer_key: Option<String>,
55 #[serde(default, skip_serializing_if = "Option::is_none")]
57 pub default_size: Option<CanvasSize>,
58
59 #[serde(default, skip_serializing_if = "Vec::is_empty")]
61 pub ports: Vec<PortDecl>,
62
63 #[serde(default)]
65 pub default_data: Value,
66}
67
68#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
70pub enum NodeInstantiationError {
71 #[error("node kind schema not found: {0:?}")]
73 MissingSchema(NodeKindKey),
74 #[error("port id count mismatch: expected {expected}, got {actual}")]
76 PortIdCountMismatch { expected: usize, actual: usize },
77}
78
79#[derive(Debug, Clone)]
81pub struct NodeInstantiation {
82 pub node_id: NodeId,
84 pub node: Node,
86 pub ports: Vec<(PortId, Port)>,
88}
89
90impl NodeInstantiation {
91 pub fn into_parts(self) -> (NodeId, Node, Vec<(PortId, Port)>) {
93 (self.node_id, self.node, self.ports)
94 }
95
96 pub fn into_ops(self) -> Vec<GraphOp> {
98 let port_order = self.node.ports.clone();
99 let mut node = self.node;
100 node.ports = Vec::new();
101
102 let mut ops =
103 Vec::with_capacity(self.ports.len() + usize::from(!port_order.is_empty()) + 1);
104 ops.push(GraphOp::AddNode {
105 id: self.node_id,
106 node,
107 });
108 ops.extend(
109 self.ports
110 .into_iter()
111 .map(|(id, port)| GraphOp::AddPort { id, port }),
112 );
113 if !port_order.is_empty() {
114 ops.push(GraphOp::SetNodePorts {
115 id: self.node_id,
116 from: Vec::new(),
117 to: port_order,
118 });
119 }
120 ops
121 }
122
123 pub fn into_transaction(self) -> GraphTransaction {
125 GraphTransaction::from_ops(self.into_ops())
126 }
127
128 pub fn into_labeled_transaction(self, label: impl Into<String>) -> GraphTransaction {
130 self.into_transaction().with_label(label)
131 }
132}
133
134impl NodeSchema {
135 pub fn instantiate(&self, pos: CanvasPoint) -> NodeInstantiation {
137 let node_id = NodeId::new();
138 let port_ids = std::iter::repeat_with(PortId::new)
139 .take(self.ports.len())
140 .collect();
141 self.instantiate_from_port_ids(node_id, pos, port_ids)
142 }
143
144 pub fn instantiate_with_ids(
146 &self,
147 node_id: NodeId,
148 pos: CanvasPoint,
149 port_ids: impl IntoIterator<Item = PortId>,
150 ) -> Result<NodeInstantiation, NodeInstantiationError> {
151 let port_ids: Vec<PortId> = port_ids.into_iter().collect();
152 if port_ids.len() != self.ports.len() {
153 return Err(NodeInstantiationError::PortIdCountMismatch {
154 expected: self.ports.len(),
155 actual: port_ids.len(),
156 });
157 }
158
159 Ok(self.instantiate_from_port_ids(node_id, pos, port_ids))
160 }
161
162 fn instantiate_from_port_ids(
163 &self,
164 node_id: NodeId,
165 pos: CanvasPoint,
166 port_ids: Vec<PortId>,
167 ) -> NodeInstantiation {
168 let ports = self
169 .ports
170 .iter()
171 .zip(port_ids.iter().copied())
172 .map(|(decl, port_id)| (port_id, decl.instantiate(node_id)))
173 .collect();
174
175 NodeInstantiation {
176 node_id,
177 node: Node {
178 kind: self.kind.clone(),
179 kind_version: self.latest_kind_version,
180 pos,
181 origin: None,
182 selectable: None,
183 focusable: None,
184 draggable: None,
185 connectable: None,
186 deletable: None,
187 parent: None,
188 extent: None,
189 expand_parent: None,
190 size: self.default_size,
191 hidden: false,
192 collapsed: false,
193 ports: port_ids,
194 data: self.default_data.clone(),
195 },
196 ports,
197 }
198 }
199}
200
201impl PortDecl {
202 fn instantiate(&self, node: NodeId) -> Port {
203 Port {
204 node,
205 key: self.key.clone(),
206 dir: self.dir,
207 kind: self.kind,
208 capacity: self.capacity,
209 connectable: None,
210 connectable_start: None,
211 connectable_end: None,
212 ty: self.ty.clone(),
213 data: Value::Null,
214 }
215 }
216}
217
218#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
220pub struct NodeKindViewDescriptor {
221 pub kind: NodeKindKey,
223 pub renderer_key: String,
225 pub title: String,
227 #[serde(default, skip_serializing_if = "Vec::is_empty")]
229 pub category: Vec<String>,
230 #[serde(default, skip_serializing_if = "Vec::is_empty")]
232 pub keywords: Vec<String>,
233 #[serde(default, skip_serializing_if = "Option::is_none")]
235 pub default_size: Option<CanvasSize>,
236 #[serde(default, skip_serializing_if = "Vec::is_empty")]
238 pub ports: Vec<PortDecl>,
239 #[serde(default)]
241 pub default_data: Value,
242}
243
244impl NodeKindViewDescriptor {
245 pub(crate) fn from_schema(schema: &NodeSchema) -> Self {
246 Self {
247 kind: schema.kind.clone(),
248 renderer_key: schema
249 .renderer_key
250 .clone()
251 .unwrap_or_else(|| schema.kind.0.clone()),
252 title: schema.title.clone(),
253 category: schema.category.clone(),
254 keywords: schema.keywords.clone(),
255 default_size: schema.default_size,
256 ports: schema.ports.clone(),
257 default_data: schema.default_data.clone(),
258 }
259 }
260}