rust_logic_graph/core/
graph.rs1
2use serde::{Serialize, Deserialize};
3use std::collections::HashMap;
4
5use crate::node::NodeType;
6
7#[derive(Debug, Serialize, Deserialize, Clone)]
8pub struct Edge {
9 pub from: String,
10 pub to: String,
11 pub rule: Option<String>,
12}
13
14#[derive(Debug, Serialize, Deserialize, Clone)]
16pub struct NodeConfig {
17 pub node_type: NodeType,
18 #[serde(default)]
19 pub condition: Option<String>,
20 #[serde(default)]
21 pub query: Option<String>,
22 #[serde(default)]
23 pub prompt: Option<String>,
24}
25
26impl NodeConfig {
27 pub fn rule_node(condition: impl Into<String>) -> Self {
28 Self {
29 node_type: NodeType::RuleNode,
30 condition: Some(condition.into()),
31 query: None,
32 prompt: None,
33 }
34 }
35
36 pub fn db_node(query: impl Into<String>) -> Self {
37 Self {
38 node_type: NodeType::DBNode,
39 condition: None,
40 query: Some(query.into()),
41 prompt: None,
42 }
43 }
44
45 pub fn ai_node(prompt: impl Into<String>) -> Self {
46 Self {
47 node_type: NodeType::AINode,
48 condition: None,
49 query: None,
50 prompt: Some(prompt.into()),
51 }
52 }
53
54 pub fn grpc_node(service_url: impl Into<String>, method: impl Into<String>) -> Self {
56 Self {
57 node_type: NodeType::GrpcNode,
58 query: Some(format!("{}#{}", service_url.into(), method.into())),
59 condition: None,
60 prompt: None,
61 }
62 }
63}
64
65#[derive(Debug, Serialize, Deserialize, Clone)]
66pub struct GraphDef {
67 pub nodes: HashMap<String, NodeConfig>,
68 pub edges: Vec<Edge>,
69}
70
71impl GraphDef {
72 pub fn from_node_types(
74 nodes: HashMap<String, NodeType>,
75 edges: Vec<Edge>,
76 ) -> Self {
77 let nodes = nodes
78 .into_iter()
79 .map(|(id, node_type)| {
80 let config = match node_type {
81 NodeType::RuleNode => NodeConfig::rule_node("true"),
82 NodeType::DBNode => NodeConfig::db_node(format!("SELECT * FROM {}", id)),
83 NodeType::AINode => NodeConfig::ai_node(format!("Process data for {}", id)),
84 NodeType::GrpcNode => NodeConfig::grpc_node(
85 format!("http://localhost:50051"),
86 format!("{}_method", id)
87 ),
88 };
89 (id, config)
90 })
91 .collect();
92
93 Self { nodes, edges }
94 }
95
96 pub fn validate(&self) -> anyhow::Result<()> {
98 if self.nodes.is_empty() {
100 return Err(anyhow::anyhow!("Graph has no nodes"));
101 }
102
103 for edge in &self.edges {
105 if !self.nodes.contains_key(&edge.from) {
106 return Err(anyhow::anyhow!(
107 "Edge references non-existent source node: '{}'",
108 edge.from
109 ));
110 }
111 if !self.nodes.contains_key(&edge.to) {
112 return Err(anyhow::anyhow!(
113 "Edge references non-existent target node: '{}'",
114 edge.to
115 ));
116 }
117 }
118
119 Ok(())
120 }
121
122 pub fn has_disconnected_components(&self) -> bool {
124 if self.nodes.is_empty() {
125 return false;
126 }
127
128 use std::collections::HashSet;
129 let mut visited = HashSet::new();
130 let mut stack = Vec::new();
131
132 if let Some(first_node) = self.nodes.keys().next() {
134 stack.push(first_node.clone());
135 }
136
137 while let Some(node) = stack.pop() {
139 if visited.contains(&node) {
140 continue;
141 }
142 visited.insert(node.clone());
143
144 for edge in &self.edges {
146 if edge.from == node && !visited.contains(&edge.to) {
147 stack.push(edge.to.clone());
148 }
149 if edge.to == node && !visited.contains(&edge.from) {
150 stack.push(edge.from.clone());
151 }
152 }
153 }
154
155 visited.len() < self.nodes.len()
156 }
157}
158
159#[derive(Default)]
160pub struct Context {
161 pub data: HashMap<String, serde_json::Value>,
162}
163
164impl Context {
165 pub fn new() -> Self {
166 Self {
167 data: HashMap::new(),
168 }
169 }
170
171 pub fn set(&mut self, key: impl Into<String>, value: serde_json::Value) {
173 self.data.insert(key.into(), value);
174 }
175
176 pub fn get(&self, key: &str) -> Option<&serde_json::Value> {
177 self.data.get(key)
178 }
179
180 pub fn contains_key(&self, key: &str) -> bool {
182 self.data.contains_key(key)
183 }
184
185 pub fn remove(&mut self, key: &str) -> Option<serde_json::Value> {
187 self.data.remove(key)
188 }
189
190 pub fn clear(&mut self) {
192 self.data.clear();
193 }
194}
195
196pub struct Graph {
197 pub def: GraphDef,
198 pub context: Context,
199}
200
201impl Graph {
202 pub fn new(def: GraphDef) -> Self {
203 Self {
204 def,
205 context: Context::default(),
206 }
207 }
208}