Skip to main content

ferrum_flow/
node.rs

1use std::{collections::HashMap, fmt::Display, str::FromStr};
2
3use gpui::{Bounds, Pixels, Point, Size, px};
4use serde::{Deserialize, Serialize};
5use serde_json::json;
6use uuid::Uuid;
7
8use crate::Graph;
9
10pub const DEFAULT_NODE_WIDTH: Pixels = px(120.0);
11pub const DEFAULT_NODE_HEIGHT: Pixels = px(60.0);
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
14pub struct NodeId(Uuid);
15
16impl Display for NodeId {
17    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18        write!(f, "{}", self.0)
19    }
20}
21
22impl NodeId {
23    pub fn new() -> Self {
24        Self(Uuid::new_v4())
25    }
26    pub fn from_string(s: impl Into<String>) -> Option<Self> {
27        let string = s.into();
28        Uuid::from_str(&string).ok().map(Self)
29    }
30    pub fn from_uuid(uuid: Uuid) -> Self {
31        Self(uuid)
32    }
33
34    pub fn as_uuid(&self) -> &Uuid {
35        &self.0
36    }
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct Node {
41    pub id: NodeId,
42    pub node_type: String,
43    pub execute_type: String,
44    pub x: Pixels,
45    pub y: Pixels,
46    pub size: Size<Pixels>,
47
48    pub inputs: Vec<PortId>,
49    pub outputs: Vec<PortId>,
50    pub data: serde_json::Value,
51}
52
53impl Node {
54    pub fn new(x: f32, y: f32) -> Self {
55        Self {
56            id: NodeId::new(),
57            node_type: String::new(),
58            execute_type: String::new(),
59            x: x.into(),
60            y: y.into(),
61            size: Size {
62                width: DEFAULT_NODE_WIDTH,
63                height: DEFAULT_NODE_HEIGHT,
64            },
65            inputs: vec![],
66            outputs: vec![],
67            data: json!({}),
68        }
69    }
70
71    pub fn node_type(mut self, ty: impl Into<String>) -> Self {
72        self.node_type = ty.into();
73        self
74    }
75
76    pub fn point(&self) -> Point<Pixels> {
77        Point::new(self.x, self.y)
78    }
79
80    pub fn bounds(&self) -> Bounds<Pixels> {
81        Bounds::new(self.point(), self.size)
82    }
83
84    pub fn set_size(mut self, size: Size<Pixels>) -> Self {
85        self.size = size;
86        self
87    }
88
89    pub fn output(mut self, id: PortId) -> Self {
90        self.outputs.push(id);
91        self
92    }
93
94    pub fn input(mut self, id: PortId) -> Self {
95        self.inputs.push(id);
96        self
97    }
98}
99
100#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
101pub struct PortId(Uuid);
102
103impl Display for PortId {
104    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105        write!(f, "{}", self.0)
106    }
107}
108
109impl PortId {
110    pub fn new() -> Self {
111        Self(Uuid::new_v4())
112    }
113    pub fn from_string(s: impl Into<String>) -> Option<Self> {
114        let string = s.into();
115        Uuid::from_str(&string).ok().map(Self)
116    }
117    pub fn from_uuid(uuid: Uuid) -> Self {
118        Self(uuid)
119    }
120
121    pub fn as_uuid(&self) -> &Uuid {
122        &self.0
123    }
124}
125
126#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
127pub enum PortKind {
128    Input,
129    Output,
130}
131
132#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash)]
133pub enum PortPosition {
134    Left,
135    Right,
136    Top,
137    Bottom,
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct Port {
142    pub id: PortId,
143    pub kind: PortKind,
144    pub index: usize,
145    pub node_id: NodeId,
146    pub position: PortPosition,
147    pub size: Size<Pixels>,
148}
149
150impl ToString for PortKind {
151    fn to_string(&self) -> String {
152        match self {
153            PortKind::Input => "input".into(),
154            PortKind::Output => "output".into(),
155        }
156    }
157}
158impl ToString for PortPosition {
159    fn to_string(&self) -> String {
160        match self {
161            PortPosition::Left => "left".into(),
162            PortPosition::Right => "right".into(),
163            PortPosition::Top => "top".into(),
164            PortPosition::Bottom => "bottom".into(),
165        }
166    }
167}
168
169impl PortPosition {
170    pub fn from_str(str: &str) -> Option<Self> {
171        match str {
172            "right" => Some(Self::Right),
173            "top" => Some(Self::Top),
174            "bottom" => Some(Self::Bottom),
175            "left" => Some(Self::Left),
176            _ => None,
177        }
178    }
179}
180
181pub struct NodeBuilder {
182    node_type: String,
183    execute_type: String,
184    x: Pixels,
185    y: Pixels,
186    size: Size<Pixels>,
187    inputs: Vec<PortSpec>,
188    outputs: Vec<PortSpec>,
189    data: serde_json::Value,
190}
191
192#[derive(Clone)]
193struct PortSpec {
194    position: PortPosition,
195    size: Size<Pixels>,
196}
197
198const DEFAULT_PORT_SIZE: Size<Pixels> = Size {
199    width: px(12.0),
200    height: px(12.0),
201};
202
203impl NodeBuilder {
204    pub fn new(node_type: impl Into<String>) -> Self {
205        Self {
206            node_type: node_type.into(),
207            execute_type: String::new(),
208            x: px(0.0),
209            y: px(0.0),
210            size: Size {
211                width: DEFAULT_NODE_WIDTH,
212                height: DEFAULT_NODE_HEIGHT,
213            },
214            inputs: vec![],
215            outputs: vec![],
216            data: serde_json::Value::Null,
217        }
218    }
219
220    pub fn execute_type(mut self, execute_type: impl Into<String>) -> Self {
221        self.execute_type = execute_type.into();
222        self
223    }
224
225    pub fn position(mut self, x: f32, y: f32) -> Self {
226        self.x = x.into();
227        self.y = y.into();
228        self
229    }
230
231    pub fn size(mut self, w: f32, h: f32) -> Self {
232        self.size = Size {
233            width: w.into(),
234            height: h.into(),
235        };
236        self
237    }
238
239    pub fn input(mut self) -> Self {
240        self.inputs.push(PortSpec {
241            position: PortPosition::Left,
242            size: DEFAULT_PORT_SIZE,
243        });
244        self
245    }
246
247    pub fn output(mut self) -> Self {
248        self.outputs.push(PortSpec {
249            position: PortPosition::Right,
250            size: DEFAULT_PORT_SIZE,
251        });
252        self
253    }
254
255    pub fn input_at(mut self, pos: PortPosition) -> Self {
256        self.inputs.push(PortSpec {
257            position: pos,
258            size: DEFAULT_PORT_SIZE,
259        });
260        self
261    }
262
263    pub fn output_at(mut self, pos: PortPosition) -> Self {
264        self.outputs.push(PortSpec {
265            position: pos,
266            size: DEFAULT_PORT_SIZE,
267        });
268        self
269    }
270
271    pub fn input_with(mut self, pos: PortPosition, size: Size<Pixels>) -> Self {
272        self.inputs.push(PortSpec {
273            position: pos,
274            size,
275        });
276        self
277    }
278
279    pub fn output_with(mut self, pos: PortPosition, size: Size<Pixels>) -> Self {
280        self.outputs.push(PortSpec {
281            position: pos,
282            size,
283        });
284        self
285    }
286
287    pub fn data(mut self, data: serde_json::Value) -> Self {
288        self.data = data;
289        self
290    }
291
292    pub fn only_build(self, graph: &Graph) -> (Node, Vec<Port>) {
293        let node_id = graph.next_node_id();
294
295        let mut inputs = Vec::new();
296        let mut outputs = Vec::new();
297
298        let mut input_counters: HashMap<PortPosition, usize> = HashMap::new();
299
300        // Create input ports
301        let mut ports = vec![];
302        for spec in self.inputs {
303            let port_id = graph.next_port_id();
304
305            let index = input_counters.entry(spec.position).or_insert(0);
306            let current_index = *index;
307            *index += 1;
308
309            ports.push(Port {
310                id: port_id,
311                kind: PortKind::Input,
312                index: current_index,
313                node_id,
314                position: spec.position,
315                size: spec.size,
316            });
317
318            inputs.push(port_id);
319        }
320
321        let mut output_counters: HashMap<PortPosition, usize> = HashMap::new();
322
323        // Create output ports
324        for spec in self.outputs {
325            let port_id = graph.next_port_id();
326
327            let index = output_counters.entry(spec.position).or_insert(0);
328            let current_index = *index;
329            *index += 1;
330
331            ports.push(Port {
332                id: port_id,
333                kind: PortKind::Output,
334                index: current_index,
335                node_id,
336                position: spec.position,
337                size: spec.size,
338            });
339
340            outputs.push(port_id);
341        }
342
343        (
344            Node {
345                id: node_id,
346                node_type: self.node_type,
347                execute_type: self.execute_type,
348                x: self.x,
349                y: self.y,
350                size: self.size,
351                inputs,
352                outputs,
353                data: self.data,
354            },
355            ports,
356        )
357    }
358
359    pub fn build(self, graph: &mut Graph) -> NodeId {
360        let (node, ports) = self.only_build(graph);
361        graph.nodes.insert(node.id, node.clone());
362        graph.node_order_mut().push(node.id);
363        for port in ports {
364            graph.ports.insert(port.id, port);
365        }
366        node.id
367    }
368}