1use serde::{Deserialize, Serialize};
8use serde_json::Value;
9use std::collections::HashMap;
10
11use crate::{CompiledNode, OpCode};
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct TracedResult {
16 pub result: Value,
18 pub expression_tree: ExpressionNode,
20 pub steps: Vec<ExecutionStep>,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct ExpressionNode {
27 pub id: u32,
29 pub expression: String,
31 pub children: Vec<ExpressionNode>,
33}
34
35impl ExpressionNode {
36 pub fn build_from_compiled(node: &CompiledNode) -> (ExpressionNode, HashMap<usize, u32>) {
40 let mut id_counter = 0u32;
41 let mut node_id_map = HashMap::new();
42 let tree = Self::build_node(node, &mut id_counter, &mut node_id_map);
43 (tree, node_id_map)
44 }
45
46 fn build_node(
47 node: &CompiledNode,
48 id_counter: &mut u32,
49 node_id_map: &mut HashMap<usize, u32>,
50 ) -> ExpressionNode {
51 let id = *id_counter;
52 *id_counter += 1;
53
54 let node_ptr = node as *const CompiledNode as usize;
56 node_id_map.insert(node_ptr, id);
57
58 match node {
59 CompiledNode::Value { value, .. } => {
60 ExpressionNode {
63 id,
64 expression: value.to_string(),
65 children: vec![],
66 }
67 }
68 CompiledNode::Array { nodes, .. } => {
69 let children: Vec<ExpressionNode> = nodes
70 .iter()
71 .filter(|n| Self::is_operator_node(n))
72 .map(|n| Self::build_node(n, id_counter, node_id_map))
73 .collect();
74
75 ExpressionNode {
76 id,
77 expression: Self::node_to_json_string(node),
78 children,
79 }
80 }
81 CompiledNode::BuiltinOperator { opcode, args, .. } => {
82 let children: Vec<ExpressionNode> = args
83 .iter()
84 .filter(|n| Self::is_operator_node(n))
85 .map(|n| Self::build_node(n, id_counter, node_id_map))
86 .collect();
87
88 ExpressionNode {
89 id,
90 expression: Self::builtin_to_json_string(opcode, args),
91 children,
92 }
93 }
94 CompiledNode::CustomOperator { name, args, .. } => {
95 let children: Vec<ExpressionNode> = args
96 .iter()
97 .filter(|n| Self::is_operator_node(n))
98 .map(|n| Self::build_node(n, id_counter, node_id_map))
99 .collect();
100
101 ExpressionNode {
102 id,
103 expression: Self::custom_to_json_string(name, args),
104 children,
105 }
106 }
107 CompiledNode::StructuredObject { fields, .. } => {
108 let children: Vec<ExpressionNode> = fields
109 .iter()
110 .filter(|(_, n)| Self::is_operator_node(n))
111 .map(|(_, n)| Self::build_node(n, id_counter, node_id_map))
112 .collect();
113
114 ExpressionNode {
115 id,
116 expression: Self::structured_to_json_string(fields),
117 children,
118 }
119 }
120 }
121 }
122
123 fn is_operator_node(node: &CompiledNode) -> bool {
125 !matches!(node, CompiledNode::Value { .. })
126 }
127
128 fn node_to_json_string(node: &CompiledNode) -> String {
130 match node {
131 CompiledNode::Value { value, .. } => value.to_string(),
132 CompiledNode::Array { nodes, .. } => {
133 let items: Vec<String> = nodes.iter().map(Self::node_to_json_string).collect();
134 format!("[{}]", items.join(", "))
135 }
136 CompiledNode::BuiltinOperator { opcode, args, .. } => {
137 Self::builtin_to_json_string(opcode, args)
138 }
139 CompiledNode::CustomOperator { name, args, .. } => {
140 Self::custom_to_json_string(name, args)
141 }
142 CompiledNode::StructuredObject { fields, .. } => {
143 Self::structured_to_json_string(fields)
144 }
145 }
146 }
147
148 fn builtin_to_json_string(opcode: &OpCode, args: &[CompiledNode]) -> String {
149 let op_str = opcode.as_str();
150 let args_str = if args.len() == 1 {
151 Self::node_to_json_string(&args[0])
152 } else {
153 let items: Vec<String> = args.iter().map(Self::node_to_json_string).collect();
154 format!("[{}]", items.join(", "))
155 };
156 format!("{{\"{}\": {}}}", op_str, args_str)
157 }
158
159 fn custom_to_json_string(name: &str, args: &[CompiledNode]) -> String {
160 let args_str = if args.len() == 1 {
161 Self::node_to_json_string(&args[0])
162 } else {
163 let items: Vec<String> = args.iter().map(Self::node_to_json_string).collect();
164 format!("[{}]", items.join(", "))
165 };
166 format!("{{\"{}\": {}}}", name, args_str)
167 }
168
169 fn structured_to_json_string(fields: &[(String, CompiledNode)]) -> String {
170 let items: Vec<String> = fields
171 .iter()
172 .map(|(key, node)| format!("\"{}\": {}", key, Self::node_to_json_string(node)))
173 .collect();
174 format!("{{{}}}", items.join(", "))
175 }
176}
177
178#[derive(Debug, Clone, Serialize, Deserialize)]
180pub struct ExecutionStep {
181 pub id: u32,
183 pub node_id: u32,
185 pub context: Value,
187 pub result: Option<Value>,
189 pub error: Option<String>,
191 #[serde(skip_serializing_if = "Option::is_none")]
193 pub iteration_index: Option<u32>,
194 #[serde(skip_serializing_if = "Option::is_none")]
196 pub iteration_total: Option<u32>,
197}
198
199pub struct TraceCollector {
201 steps: Vec<ExecutionStep>,
203 step_counter: u32,
205 iteration_stack: Vec<(u32, u32)>,
207}
208
209impl TraceCollector {
210 pub fn new() -> Self {
212 Self {
213 steps: Vec::new(),
214 step_counter: 0,
215 iteration_stack: Vec::new(),
216 }
217 }
218
219 pub fn record_step(&mut self, node_id: u32, context: Value, result: Value) {
221 let (iteration_index, iteration_total) = self.current_iteration();
222 let step = ExecutionStep {
223 id: self.step_counter,
224 node_id,
225 context,
226 result: Some(result),
227 error: None,
228 iteration_index,
229 iteration_total,
230 };
231 self.steps.push(step);
232 self.step_counter += 1;
233 }
234
235 pub fn record_error(&mut self, node_id: u32, context: Value, error: String) {
237 let (iteration_index, iteration_total) = self.current_iteration();
238 let step = ExecutionStep {
239 id: self.step_counter,
240 node_id,
241 context,
242 result: None,
243 error: Some(error),
244 iteration_index,
245 iteration_total,
246 };
247 self.steps.push(step);
248 self.step_counter += 1;
249 }
250
251 pub fn push_iteration(&mut self, index: u32, total: u32) {
253 self.iteration_stack.push((index, total));
254 }
255
256 pub fn pop_iteration(&mut self) {
258 self.iteration_stack.pop();
259 }
260
261 fn current_iteration(&self) -> (Option<u32>, Option<u32>) {
263 self.iteration_stack
264 .last()
265 .map(|(i, t)| (Some(*i), Some(*t)))
266 .unwrap_or((None, None))
267 }
268
269 pub fn into_steps(self) -> Vec<ExecutionStep> {
271 self.steps
272 }
273}
274
275impl Default for TraceCollector {
276 fn default() -> Self {
277 Self::new()
278 }
279}
280
281#[cfg(test)]
282mod tests {
283 use super::*;
284 use crate::OpCode;
285
286 #[test]
287 fn test_expression_node_from_simple_operator() {
288 let node = CompiledNode::BuiltinOperator {
290 opcode: OpCode::Var,
291 args: vec![CompiledNode::Value {
292 value: serde_json::json!("age"),
293 }],
294 };
295
296 let (tree, node_id_map) = ExpressionNode::build_from_compiled(&node);
297
298 assert_eq!(tree.id, 0);
299 assert_eq!(tree.expression, r#"{"var": "age"}"#);
300 assert!(tree.children.is_empty()); assert_eq!(node_id_map.len(), 1);
302 }
303
304 #[test]
305 fn test_expression_node_from_nested_operator() {
306 let var_node = CompiledNode::BuiltinOperator {
308 opcode: OpCode::Var,
309 args: vec![CompiledNode::Value {
310 value: serde_json::json!("age"),
311 }],
312 };
313 let node = CompiledNode::BuiltinOperator {
314 opcode: OpCode::GreaterThanEqual,
315 args: vec![
316 var_node,
317 CompiledNode::Value {
318 value: serde_json::json!(18),
319 },
320 ],
321 };
322
323 let (tree, node_id_map) = ExpressionNode::build_from_compiled(&node);
324
325 assert_eq!(tree.id, 0);
326 assert!(tree.expression.contains(">="));
327 assert_eq!(tree.children.len(), 1); assert_eq!(tree.children[0].id, 1);
329 assert!(tree.children[0].expression.contains("var"));
330 assert_eq!(node_id_map.len(), 2);
331 }
332
333 #[test]
334 fn test_trace_collector_records_steps() {
335 let mut collector = TraceCollector::new();
336
337 collector.record_step(0, serde_json::json!({"age": 25}), serde_json::json!(25));
338 collector.record_step(1, serde_json::json!({"age": 25}), serde_json::json!(true));
339
340 let steps = collector.into_steps();
341 assert_eq!(steps.len(), 2);
342 assert_eq!(steps[0].id, 0);
343 assert_eq!(steps[0].node_id, 0);
344 assert_eq!(steps[1].id, 1);
345 assert_eq!(steps[1].node_id, 1);
346 }
347
348 #[test]
349 fn test_trace_collector_iteration_context() {
350 let mut collector = TraceCollector::new();
351
352 collector.push_iteration(0, 3);
353 collector.record_step(2, serde_json::json!(1), serde_json::json!(2));
354
355 let steps = collector.into_steps();
356 assert_eq!(steps[0].iteration_index, Some(0));
357 assert_eq!(steps[0].iteration_total, Some(3));
358 }
359}