jellyflow_core/core/
symbol_ref.rs1use std::collections::BTreeSet;
2
3use serde_json::Value;
4use uuid::Uuid;
5
6use super::{Graph, Node, NodeId, SymbolId};
7
8pub const SYMBOL_REF_NODE_KIND: &str = "jellyflow.symbol_ref";
12
13#[derive(Debug, Clone, thiserror::Error, PartialEq, Eq)]
14pub enum SymbolRefNodeError {
15 #[error("symbol ref node missing symbol_id: node={node:?}")]
16 MissingSymbolId { node: NodeId },
17
18 #[error("symbol ref node symbol_id is not a string: node={node:?}")]
19 SymbolIdNotString { node: NodeId },
20
21 #[error("symbol ref node symbol_id is not a valid uuid: node={node:?} value={value:?}")]
22 InvalidSymbolId { node: NodeId, value: String },
23}
24
25#[derive(Debug, Clone, thiserror::Error, PartialEq, Eq)]
26pub enum SymbolRefBindingError {
27 #[error(
28 "symbol ref target is not declared in graph symbols: node={node:?} symbol_id={symbol_id:?}"
29 )]
30 TargetNotDeclared { node: NodeId, symbol_id: SymbolId },
31}
32
33pub fn is_symbol_ref_node(node: &Node) -> bool {
34 node.kind.0 == SYMBOL_REF_NODE_KIND
35}
36
37pub fn symbol_ref_target_symbol_id(
43 node_id: NodeId,
44 node: &Node,
45) -> Result<Option<SymbolId>, SymbolRefNodeError> {
46 if !is_symbol_ref_node(node) {
47 return Ok(None);
48 }
49
50 let Some(obj) = node.data.as_object() else {
51 return Err(SymbolRefNodeError::MissingSymbolId { node: node_id });
52 };
53
54 let Some(raw) = obj.get("symbol_id") else {
55 return Err(SymbolRefNodeError::MissingSymbolId { node: node_id });
56 };
57
58 let Some(s) = raw.as_str() else {
59 return Err(SymbolRefNodeError::SymbolIdNotString { node: node_id });
60 };
61
62 let uuid = Uuid::parse_str(s).map_err(|_| SymbolRefNodeError::InvalidSymbolId {
63 node: node_id,
64 value: s.to_string(),
65 })?;
66
67 Ok(Some(SymbolId(uuid)))
68}
69
70pub fn collect_symbol_ref_targets(graph: &Graph) -> Result<BTreeSet<SymbolId>, SymbolRefNodeError> {
74 let mut out = BTreeSet::new();
75 for (node_id, node) in &graph.nodes {
76 if let Some(target) = symbol_ref_target_symbol_id(*node_id, node)? {
77 out.insert(target);
78 }
79 }
80 Ok(out)
81}
82
83pub fn validate_symbol_ref_targets_are_declared(
85 graph: &Graph,
86) -> Result<(), Vec<SymbolRefBindingError>> {
87 let mut errors = Vec::new();
88 for (node_id, node) in &graph.nodes {
89 let Ok(Some(target)) = symbol_ref_target_symbol_id(*node_id, node) else {
90 continue;
91 };
92 if !graph.symbols.contains_key(&target) {
93 errors.push(SymbolRefBindingError::TargetNotDeclared {
94 node: *node_id,
95 symbol_id: target,
96 });
97 }
98 }
99
100 if errors.is_empty() {
101 Ok(())
102 } else {
103 Err(errors)
104 }
105}
106
107pub fn symbol_ref_node_data(symbol_id: SymbolId) -> Value {
109 serde_json::json!({ "symbol_id": symbol_id })
110}