Skip to main content

jellyflow_core/core/model/
builder.rs

1use std::ops::Deref;
2
3use crate::core::ids::{
4    BindingId, EdgeId, GraphId, GroupId, NodeId, PortId, StickyNoteId, SymbolId,
5};
6use crate::core::imports::GraphImport;
7use crate::core::validate::{GraphValidationError, validate_graph};
8
9use super::binding::Binding;
10use super::edge::Edge;
11use super::graph::Graph;
12use super::node::Node;
13use super::port::Port;
14use super::resources::{Group, StickyNote, Symbol};
15
16/// Builder for assembling an initial graph document before handing it to runtime mutation APIs.
17///
18/// The builder keeps direct collection writes out of `Graph`'s public API while still making
19/// fixtures, importers, and graph generators straightforward. Use [`GraphBuilder::build`] when the
20/// result should satisfy graph invariants, or [`GraphBuilder::build_unchecked`] only for tests and
21/// migrations that intentionally inspect invalid graphs.
22#[derive(Debug, Clone, Default)]
23pub struct GraphBuilder {
24    graph: Graph,
25}
26
27impl GraphBuilder {
28    /// Creates an empty builder for the given graph id.
29    pub fn new(graph_id: GraphId) -> Self {
30        Self {
31            graph: Graph::new(graph_id),
32        }
33    }
34
35    /// Starts from an existing graph.
36    pub(crate) fn from_graph(graph: Graph) -> Self {
37        Self { graph }
38    }
39
40    /// Returns the graph id being built.
41    pub fn graph_id(&self) -> GraphId {
42        self.graph.graph_id()
43    }
44
45    /// Borrows the current graph snapshot.
46    pub fn graph(&self) -> &Graph {
47        &self.graph
48    }
49
50    /// Adds or replaces an import.
51    pub fn with_import(mut self, id: GraphId, import: GraphImport) -> Self {
52        self.insert_import(id, import);
53        self
54    }
55
56    /// Adds or replaces a symbol.
57    pub fn with_symbol(mut self, id: SymbolId, symbol: Symbol) -> Self {
58        self.insert_symbol(id, symbol);
59        self
60    }
61
62    /// Adds or replaces a node.
63    pub fn with_node(mut self, id: NodeId, node: Node) -> Self {
64        self.insert_node(id, node);
65        self
66    }
67
68    /// Adds or replaces a port.
69    pub fn with_port(mut self, id: PortId, port: Port) -> Self {
70        self.insert_port(id, port);
71        self
72    }
73
74    /// Adds or replaces an edge.
75    pub fn with_edge(mut self, id: EdgeId, edge: Edge) -> Self {
76        self.insert_edge(id, edge);
77        self
78    }
79
80    /// Adds or replaces a group.
81    pub fn with_group(mut self, id: GroupId, group: Group) -> Self {
82        self.insert_group(id, group);
83        self
84    }
85
86    /// Adds or replaces a sticky note.
87    pub fn with_sticky_note(mut self, id: StickyNoteId, note: StickyNote) -> Self {
88        self.insert_sticky_note(id, note);
89        self
90    }
91
92    /// Adds or replaces a binding.
93    pub fn with_binding(mut self, id: BindingId, binding: Binding) -> Self {
94        self.insert_binding(id, binding);
95        self
96    }
97
98    /// Inserts or replaces an import in-place.
99    pub fn insert_import(&mut self, id: GraphId, import: GraphImport) -> Option<GraphImport> {
100        self.graph.insert_import(id, import)
101    }
102
103    /// Updates an import in-place.
104    pub fn update_import<R>(
105        &mut self,
106        id: &GraphId,
107        f: impl FnOnce(&mut GraphImport) -> R,
108    ) -> Option<R> {
109        self.graph.update_import(id, f)
110    }
111
112    /// Removes an import in-place.
113    pub fn remove_import(&mut self, id: &GraphId) -> Option<GraphImport> {
114        self.graph.remove_import(id)
115    }
116
117    /// Clears imports in-place.
118    pub fn clear_imports(&mut self) {
119        self.graph.clear_imports();
120    }
121
122    /// Retains imports matching `f`.
123    pub fn retain_imports(&mut self, f: impl FnMut(&GraphId, &mut GraphImport) -> bool) {
124        self.graph.retain_imports(f);
125    }
126
127    /// Inserts or replaces a symbol in-place.
128    pub fn insert_symbol(&mut self, id: SymbolId, symbol: Symbol) -> Option<Symbol> {
129        self.graph.insert_symbol(id, symbol)
130    }
131
132    /// Updates a symbol in-place.
133    pub fn update_symbol<R>(
134        &mut self,
135        id: &SymbolId,
136        f: impl FnOnce(&mut Symbol) -> R,
137    ) -> Option<R> {
138        self.graph.update_symbol(id, f)
139    }
140
141    /// Removes a symbol in-place.
142    pub fn remove_symbol(&mut self, id: &SymbolId) -> Option<Symbol> {
143        self.graph.remove_symbol(id)
144    }
145
146    /// Clears symbols in-place.
147    pub fn clear_symbols(&mut self) {
148        self.graph.clear_symbols();
149    }
150
151    /// Retains symbols matching `f`.
152    pub fn retain_symbols(&mut self, f: impl FnMut(&SymbolId, &mut Symbol) -> bool) {
153        self.graph.retain_symbols(f);
154    }
155
156    /// Inserts or replaces a node in-place.
157    pub fn insert_node(&mut self, id: NodeId, node: Node) -> Option<Node> {
158        self.graph.insert_node(id, node)
159    }
160
161    /// Updates a node in-place.
162    pub fn update_node<R>(&mut self, id: &NodeId, f: impl FnOnce(&mut Node) -> R) -> Option<R> {
163        self.graph.update_node(id, f)
164    }
165
166    /// Removes a node in-place.
167    pub fn remove_node(&mut self, id: &NodeId) -> Option<Node> {
168        self.graph.remove_node(id)
169    }
170
171    /// Clears nodes in-place.
172    pub fn clear_nodes(&mut self) {
173        self.graph.clear_nodes();
174    }
175
176    /// Retains nodes matching `f`.
177    pub fn retain_nodes(&mut self, f: impl FnMut(&NodeId, &mut Node) -> bool) {
178        self.graph.retain_nodes(f);
179    }
180
181    /// Inserts or replaces a port in-place.
182    pub fn insert_port(&mut self, id: PortId, port: Port) -> Option<Port> {
183        self.graph.insert_port(id, port)
184    }
185
186    /// Updates a port in-place.
187    pub fn update_port<R>(&mut self, id: &PortId, f: impl FnOnce(&mut Port) -> R) -> Option<R> {
188        self.graph.update_port(id, f)
189    }
190
191    /// Removes a port in-place.
192    pub fn remove_port(&mut self, id: &PortId) -> Option<Port> {
193        self.graph.remove_port(id)
194    }
195
196    /// Clears ports in-place.
197    pub fn clear_ports(&mut self) {
198        self.graph.clear_ports();
199    }
200
201    /// Retains ports matching `f`.
202    pub fn retain_ports(&mut self, f: impl FnMut(&PortId, &mut Port) -> bool) {
203        self.graph.retain_ports(f);
204    }
205
206    /// Inserts or replaces an edge in-place.
207    pub fn insert_edge(&mut self, id: EdgeId, edge: Edge) -> Option<Edge> {
208        self.graph.insert_edge(id, edge)
209    }
210
211    /// Updates an edge in-place.
212    pub fn update_edge<R>(&mut self, id: &EdgeId, f: impl FnOnce(&mut Edge) -> R) -> Option<R> {
213        self.graph.update_edge(id, f)
214    }
215
216    /// Removes an edge in-place.
217    pub fn remove_edge(&mut self, id: &EdgeId) -> Option<Edge> {
218        self.graph.remove_edge(id)
219    }
220
221    /// Clears edges in-place.
222    pub fn clear_edges(&mut self) {
223        self.graph.clear_edges();
224    }
225
226    /// Retains edges matching `f`.
227    pub fn retain_edges(&mut self, f: impl FnMut(&EdgeId, &mut Edge) -> bool) {
228        self.graph.retain_edges(f);
229    }
230
231    /// Inserts or replaces a group in-place.
232    pub fn insert_group(&mut self, id: GroupId, group: Group) -> Option<Group> {
233        self.graph.insert_group(id, group)
234    }
235
236    /// Updates a group in-place.
237    pub fn update_group<R>(&mut self, id: &GroupId, f: impl FnOnce(&mut Group) -> R) -> Option<R> {
238        self.graph.update_group(id, f)
239    }
240
241    /// Removes a group in-place.
242    pub fn remove_group(&mut self, id: &GroupId) -> Option<Group> {
243        self.graph.remove_group(id)
244    }
245
246    /// Clears groups in-place.
247    pub fn clear_groups(&mut self) {
248        self.graph.clear_groups();
249    }
250
251    /// Retains groups matching `f`.
252    pub fn retain_groups(&mut self, f: impl FnMut(&GroupId, &mut Group) -> bool) {
253        self.graph.retain_groups(f);
254    }
255
256    /// Inserts or replaces a sticky note in-place.
257    pub fn insert_sticky_note(&mut self, id: StickyNoteId, note: StickyNote) -> Option<StickyNote> {
258        self.graph.insert_sticky_note(id, note)
259    }
260
261    /// Updates a sticky note in-place.
262    pub fn update_sticky_note<R>(
263        &mut self,
264        id: &StickyNoteId,
265        f: impl FnOnce(&mut StickyNote) -> R,
266    ) -> Option<R> {
267        self.graph.update_sticky_note(id, f)
268    }
269
270    /// Removes a sticky note in-place.
271    pub fn remove_sticky_note(&mut self, id: &StickyNoteId) -> Option<StickyNote> {
272        self.graph.remove_sticky_note(id)
273    }
274
275    /// Clears sticky notes in-place.
276    pub fn clear_sticky_notes(&mut self) {
277        self.graph.clear_sticky_notes();
278    }
279
280    /// Retains sticky notes matching `f`.
281    pub fn retain_sticky_notes(&mut self, f: impl FnMut(&StickyNoteId, &mut StickyNote) -> bool) {
282        self.graph.retain_sticky_notes(f);
283    }
284
285    /// Inserts or replaces a binding in-place.
286    pub fn insert_binding(&mut self, id: BindingId, binding: Binding) -> Option<Binding> {
287        self.graph.insert_binding(id, binding)
288    }
289
290    /// Updates a binding in-place.
291    pub fn update_binding<R>(
292        &mut self,
293        id: &BindingId,
294        f: impl FnOnce(&mut Binding) -> R,
295    ) -> Option<R> {
296        self.graph.update_binding(id, f)
297    }
298
299    /// Removes a binding in-place.
300    pub fn remove_binding(&mut self, id: &BindingId) -> Option<Binding> {
301        self.graph.remove_binding(id)
302    }
303
304    /// Clears bindings in-place.
305    pub fn clear_bindings(&mut self) {
306        self.graph.clear_bindings();
307    }
308
309    /// Retains bindings matching `f`.
310    pub fn retain_bindings(&mut self, f: impl FnMut(&BindingId, &mut Binding) -> bool) {
311        self.graph.retain_bindings(f);
312    }
313
314    /// Validates and returns the assembled graph.
315    pub fn build(self) -> Result<Graph, Vec<GraphValidationError>> {
316        let report = validate_graph(&self.graph);
317        if report.is_ok() {
318            Ok(self.graph)
319        } else {
320            Err(report.into_errors())
321        }
322    }
323
324    /// Returns the graph without validation.
325    ///
326    /// Prefer [`GraphBuilder::build`] for normal construction. This is useful for tests and
327    /// migration tooling that intentionally produce invalid graphs and then assert diagnostics.
328    pub fn build_unchecked(self) -> Graph {
329        self.graph
330    }
331}
332
333impl Deref for GraphBuilder {
334    type Target = Graph;
335
336    fn deref(&self) -> &Self::Target {
337        &self.graph
338    }
339}
340
341impl AsRef<Graph> for GraphBuilder {
342    fn as_ref(&self) -> &Graph {
343        &self.graph
344    }
345}
346
347impl From<GraphBuilder> for Graph {
348    fn from(builder: GraphBuilder) -> Self {
349        builder.build_unchecked()
350    }
351}