Skip to main content

json_eval_rs/jsoneval/
logic.rs

1use super::JSONEval;
2use crate::jsoneval::json_parser;
3use crate::rlogic::{compiled_logic_store, CompiledLogicId, Evaluator};
4use crate::utils::clean_float_noise;
5use serde_json::Value;
6
7impl JSONEval {
8    /// Run pre-compiled logic against current data
9    pub fn run_logic(
10        &mut self,
11        logic_id: CompiledLogicId,
12        data: Option<&Value>,
13        context: Option<&Value>,
14    ) -> Result<Value, String> {
15        // Get compiled logic from global store
16        let compiled_logic = compiled_logic_store::get_compiled_logic(logic_id)
17            .ok_or_else(|| format!("Compiled logic ID {:?} not found in store", logic_id))?;
18
19        // If custom data/context provided, update eval_data
20        let run_data = if let Some(input_data) = data {
21            let context_value = context.unwrap_or(&self.context);
22            self.eval_data
23                .replace_data_and_context(input_data.clone(), context_value.clone());
24            self.eval_data.data()
25        } else {
26            self.eval_data.data()
27        };
28
29        // Create an evaluator and run the pre-compiled logic with zero-clone pattern
30        let evaluator = Evaluator::new();
31        let result = evaluator
32            .evaluate(&compiled_logic, run_data)
33            .map_err(|e| format!("Execution error: {}", e))?;
34
35        Ok(clean_float_noise(result))
36    }
37
38    /// Compile a logic expression from a JSON string and store it globally
39    pub fn compile_logic(&self, logic_str: &str) -> Result<CompiledLogicId, String> {
40        compiled_logic_store::compile_logic(logic_str)
41    }
42
43    /// Compile a logic expression from a Value and store it globally
44    pub fn compile_logic_value(&self, logic: &Value) -> Result<CompiledLogicId, String> {
45        compiled_logic_store::compile_logic_value(logic)
46    }
47
48    /// Compile and run logic in one go (convenience method)
49    pub fn compile_and_run_logic(
50        &mut self,
51        logic_str: &str,
52        data: Option<&str>,
53        context: Option<&str>,
54    ) -> Result<Value, String> {
55        let id = self.compile_logic(logic_str)?;
56
57        // Parse data and context if provided
58        let data_value = if let Some(d) = data {
59            Some(json_parser::parse_json_str(d)?)
60        } else {
61            None
62        };
63
64        let context_value = if let Some(c) = context {
65            Some(json_parser::parse_json_str(c)?)
66        } else {
67            None
68        };
69
70        self.run_logic(id, data_value.as_ref(), context_value.as_ref())
71    }
72}
73
74/// Run logic evaluation directly without schema/form state
75///
76/// This is a "pure" evaluation that doesn't rely on JSONEval instance state.
77/// It creates a temporary evaluator and runs the logic against the provided data.
78///
79/// # Arguments
80/// * `logic_str` - JSON logic expression string
81/// * `data_str` - Data JSON string (optional)
82/// * `context_str` - Context JSON string (optional). Will be merged into data under "$context" key.
83pub fn evaluate_logic_pure(
84    logic_str: &str,
85    data_str: Option<&str>,
86    context_str: Option<&str>,
87) -> Result<Value, String> {
88    // Compile logic
89    let logic_value =
90        json_parser::parse_json_str(logic_str).map_err(|e| format!("Invalid logic JSON: {}", e))?;
91    let compiled = crate::rlogic::CompiledLogic::compile(&logic_value)
92        .map_err(|e| format!("Logic compilation failed: {}", e))?;
93
94    // Parse data
95    let mut data_value = if let Some(d) = data_str {
96        json_parser::parse_json_str(d).map_err(|e| format!("Invalid data JSON: {}", e))?
97    } else {
98        Value::Null
99    };
100
101    // Merge context if provided
102    if let Some(c) = context_str {
103        let context_value =
104            json_parser::parse_json_str(c).map_err(|e| format!("Invalid context JSON: {}", e))?;
105
106        // Ensure data is an object to merge context
107        if data_value.is_null() {
108            let mut map = serde_json::Map::new();
109            map.insert("$context".to_string(), context_value);
110            data_value = Value::Object(map);
111        } else if let Some(obj) = data_value.as_object_mut() {
112            obj.insert("$context".to_string(), context_value);
113        }
114        // Note: If data is a primitive value, context cannot be merged
115    }
116
117    // Run evaluation
118    let evaluator = Evaluator::new();
119    evaluator
120        .evaluate(&compiled, &data_value)
121        .map(|v| clean_float_noise(v))
122        .map_err(|e| format!("Evaluation error: {}", e))
123}