Skip to main content

rustbasic_core/template/
evaluator.rs

1use super::parser::{Expr, Node};
2use serde_json::Value;
3use std::collections::HashMap;
4use std::sync::Arc;
5
6pub type FilterFn = Arc<dyn Fn(&Value, &[Value]) -> Value + Send + Sync>;
7
8pub struct Evaluator {
9    filters: HashMap<String, FilterFn>,
10}
11
12impl Evaluator {
13    pub fn new(filters: HashMap<String, FilterFn>) -> Self {
14        Self { filters }
15    }
16
17    pub fn evaluate_expr(&self, expr: &Expr, context: &Value) -> Value {
18        match expr {
19            Expr::StringLiteral(s) => Value::String(s.clone()),
20            Expr::NumberLiteral(n) => {
21                if let Some(num) = serde_json::Number::from_f64(*n) {
22                    Value::Number(num)
23                } else {
24                    Value::Null
25                }
26            }
27            Expr::BooleanLiteral(b) => Value::Bool(*b),
28            Expr::Path(path) => {
29                let mut current = context;
30                for segment in path {
31                    if let Value::Object(map) = current {
32                        if let Some(next) = map.get(segment) {
33                            current = next;
34                        } else {
35                            return Value::Null;
36                        }
37                    } else {
38                        return Value::Null;
39                    }
40                }
41                current.clone()
42            }
43            Expr::Comparison { left, op, right } => {
44                let left_val = self.evaluate_expr(left, context);
45                let right_val = self.evaluate_expr(right, context);
46
47                let res = match op.as_str() {
48                    "==" => left_val == right_val,
49                    "!=" => left_val != right_val,
50                    "<" => match (&left_val, &right_val) {
51                        (Value::Number(l), Value::Number(r)) => l.as_f64() < r.as_f64(),
52                        (Value::String(l), Value::String(r)) => l < r,
53                        _ => false,
54                    },
55                    ">" => match (&left_val, &right_val) {
56                        (Value::Number(l), Value::Number(r)) => l.as_f64() > r.as_f64(),
57                        (Value::String(l), Value::String(r)) => l > r,
58                        _ => false,
59                    },
60                    "<=" => match (&left_val, &right_val) {
61                        (Value::Number(l), Value::Number(r)) => l.as_f64() <= r.as_f64(),
62                        (Value::String(l), Value::String(r)) => l <= r,
63                        _ => false,
64                    },
65                    ">=" => match (&left_val, &right_val) {
66                        (Value::Number(l), Value::Number(r)) => l.as_f64() >= r.as_f64(),
67                        (Value::String(l), Value::String(r)) => l >= r,
68                        _ => false,
69                    },
70                    _ => false,
71                };
72                Value::Bool(res)
73            }
74            Expr::Filter { expr, filter_name, args } => {
75                let val = self.evaluate_expr(expr, context);
76                let evaluated_args: Vec<Value> = args.iter()
77                    .map(|a| self.evaluate_expr(a, context))
78                    .collect();
79
80                if let Some(filter_fn) = self.filters.get(filter_name) {
81                    filter_fn(&val, &evaluated_args)
82                } else {
83                    val
84                }
85            }
86        }
87    }
88
89    pub fn render_nodes(&self, nodes: &[Node], context: &Value) -> Result<String, String> {
90        let mut output = String::new();
91        for node in nodes {
92            match node {
93                Node::Text(t) => {
94                    output.push_str(t);
95                }
96                Node::Variable(expr) => {
97                    let val = self.evaluate_expr(expr, context);
98                    match val {
99                        Value::Null => {}
100                        Value::String(s) => output.push_str(&s),
101                        Value::Number(n) => output.push_str(&n.to_string()),
102                        Value::Bool(b) => output.push_str(&b.to_string()),
103                        other => {
104                            output.push_str(&other.to_string());
105                        }
106                    }
107                }
108                Node::If { condition, then_branch, else_branch } => {
109                    let cond_val = self.evaluate_expr(condition, context);
110                    let is_truthy = match cond_val {
111                        Value::Null => false,
112                        Value::Bool(b) => b,
113                        Value::Number(n) => n.as_f64().unwrap_or(0.0) != 0.0,
114                        Value::String(s) => !s.is_empty(),
115                        Value::Array(a) => !a.is_empty(),
116                        Value::Object(o) => !o.is_empty(),
117                    };
118
119                    if is_truthy {
120                        let sub = self.render_nodes(then_branch, context)?;
121                        output.push_str(&sub);
122                    } else if let Some(else_nodes) = else_branch {
123                        let sub = self.render_nodes(else_nodes, context)?;
124                        output.push_str(&sub);
125                    }
126                }
127                Node::For { item, iterator, body } => {
128                    let iter_val = self.evaluate_expr(iterator, context);
129                    if let Value::Array(list) = iter_val {
130                        for val in list {
131                            let mut local_context = context.clone();
132                            if let Value::Object(ref mut map) = local_context {
133                                map.insert(item.clone(), val.clone());
134                            } else {
135                                let mut map = serde_json::Map::new();
136                                map.insert(item.clone(), val.clone());
137                                local_context = Value::Object(map);
138                            }
139                            let sub = self.render_nodes(body, &local_context)?;
140                            output.push_str(&sub);
141                        }
142                    }
143                }
144            }
145        }
146        Ok(output)
147    }
148}