jellyflow_core/core/model/
edge.rs1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use crate::core::ids::PortId;
5
6use super::port::PortKind;
7
8fn is_false(v: &bool) -> bool {
9 !*v
10}
11
12fn edge_view_descriptor_is_default(value: &EdgeViewDescriptor) -> bool {
13 value.is_default()
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
18#[serde(rename_all = "snake_case")]
19pub enum EdgeKind {
20 Data,
22 Exec,
24}
25
26impl EdgeKind {
27 pub fn port_kind(self) -> PortKind {
28 match self {
29 Self::Data => PortKind::Data,
30 Self::Exec => PortKind::Exec,
31 }
32 }
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
37#[serde(rename_all = "snake_case")]
38pub enum EdgeRouteKind {
39 Straight,
41 Orthogonal,
43 Bezier,
45 SmoothStep,
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct Edge {
52 pub kind: EdgeKind,
54 pub from: PortId,
56 pub to: PortId,
58 #[serde(default, skip_serializing_if = "is_false")]
62 pub hidden: bool,
63
64 #[serde(default, skip_serializing_if = "Option::is_none")]
68 pub selectable: Option<bool>,
69
70 #[serde(default, skip_serializing_if = "Option::is_none")]
74 pub focusable: Option<bool>,
75
76 #[serde(default, skip_serializing_if = "Option::is_none")]
80 pub interaction_width: Option<f32>,
81
82 #[serde(default, skip_serializing_if = "Option::is_none")]
86 pub deletable: Option<bool>,
87
88 #[serde(default, skip_serializing_if = "Option::is_none")]
96 pub reconnectable: Option<EdgeReconnectable>,
97
98 #[serde(default)]
103 pub data: Value,
104
105 #[serde(default, skip_serializing_if = "edge_view_descriptor_is_default")]
110 pub view: EdgeViewDescriptor,
111}
112
113impl Edge {
114 pub fn new(kind: EdgeKind, from: PortId, to: PortId) -> Self {
116 Self {
117 kind,
118 from,
119 to,
120 hidden: false,
121 selectable: None,
122 focusable: None,
123 interaction_width: None,
124 deletable: None,
125 reconnectable: None,
126 data: Value::Null,
127 view: EdgeViewDescriptor::default(),
128 }
129 }
130}
131
132#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
134pub struct EdgeViewDescriptor {
135 #[serde(default, skip_serializing_if = "Option::is_none")]
137 pub renderer_key: Option<String>,
138 #[serde(default, skip_serializing_if = "Option::is_none")]
140 pub label: Option<String>,
141 #[serde(default, skip_serializing_if = "Option::is_none")]
143 pub label_anchor: Option<EdgeLabelAnchor>,
144 #[serde(default, skip_serializing_if = "Option::is_none")]
146 pub source_marker_key: Option<String>,
147 #[serde(default, skip_serializing_if = "Option::is_none")]
149 pub target_marker_key: Option<String>,
150 #[serde(default, skip_serializing_if = "Option::is_none")]
152 pub style_token: Option<String>,
153 #[serde(default, skip_serializing_if = "Option::is_none")]
155 pub route_kind: Option<EdgeRouteKind>,
156 #[serde(default, skip_serializing_if = "Option::is_none")]
158 pub hit_target_width: Option<f32>,
159}
160
161impl EdgeViewDescriptor {
162 pub fn new() -> Self {
163 Self::default()
164 }
165
166 pub fn with_renderer_key(mut self, renderer_key: impl Into<String>) -> Self {
167 self.renderer_key = Some(renderer_key.into());
168 self
169 }
170
171 pub fn with_label(mut self, label: impl Into<String>) -> Self {
172 self.label = Some(label.into());
173 self
174 }
175
176 pub fn with_label_anchor(mut self, label_anchor: EdgeLabelAnchor) -> Self {
177 self.label_anchor = Some(label_anchor);
178 self
179 }
180
181 pub fn with_source_marker_key(mut self, source_marker_key: impl Into<String>) -> Self {
182 self.source_marker_key = Some(source_marker_key.into());
183 self
184 }
185
186 pub fn with_target_marker_key(mut self, target_marker_key: impl Into<String>) -> Self {
187 self.target_marker_key = Some(target_marker_key.into());
188 self
189 }
190
191 pub fn with_style_token(mut self, style_token: impl Into<String>) -> Self {
192 self.style_token = Some(style_token.into());
193 self
194 }
195
196 pub fn with_route_kind(mut self, route_kind: EdgeRouteKind) -> Self {
197 self.route_kind = Some(route_kind);
198 self
199 }
200
201 pub fn with_hit_target_width(mut self, hit_target_width: f32) -> Self {
202 self.hit_target_width = Some(hit_target_width);
203 self
204 }
205
206 pub fn is_default(&self) -> bool {
207 self == &Self::default()
208 }
209}
210
211#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
213#[serde(rename_all = "snake_case")]
214pub enum EdgeLabelAnchor {
215 Source,
216 Center,
217 Target,
218}
219
220#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
222#[serde(untagged)]
223pub enum EdgeReconnectable {
224 Bool(bool),
225 Endpoint(EdgeReconnectableEndpoint),
226}
227
228impl EdgeReconnectable {
229 pub fn allows_source(self) -> bool {
230 matches!(
231 self,
232 Self::Bool(true) | Self::Endpoint(EdgeReconnectableEndpoint::Source)
233 )
234 }
235
236 pub fn allows_target(self) -> bool {
237 matches!(
238 self,
239 Self::Bool(true) | Self::Endpoint(EdgeReconnectableEndpoint::Target)
240 )
241 }
242
243 pub fn endpoint_flags(self) -> (bool, bool) {
244 (self.allows_source(), self.allows_target())
245 }
246}
247
248#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
250#[serde(rename_all = "snake_case")]
251pub enum EdgeReconnectableEndpoint {
252 Source,
253 Target,
254}