datalogic_rs/lib.rs
1//! # datalogic-rs
2//!
3//! A high-performance, thread-safe Rust implementation of JSONLogic.
4//!
5//! ## Overview
6//!
7//! `datalogic-rs` provides a powerful rule evaluation engine that compiles JSONLogic
8//! expressions into optimized, reusable structures that can be evaluated across
9//! multiple threads with zero overhead.
10//!
11//! ## Key Features
12//!
13//! - **Compilation-based optimization**: Parse once, evaluate many times
14//! - **Thread-safe by design**: Share compiled logic across threads with `Arc`
15//! - **50+ built-in operators**: Complete JSONLogic compatibility plus extensions
16//! - **Zero-copy operations**: Minimize allocations with `Cow` types
17//! - **Extensible**: Add custom operators via the `Operator` trait
18//! - **Structured templates**: Preserve object structure for dynamic outputs
19//!
20//! ## Quick Start
21//!
22//! ```rust
23//! use datalogic_rs::DataLogic;
24//! use serde_json::json;
25//!
26//! let engine = DataLogic::new();
27//!
28//! // Compile your logic once
29//! let logic = json!({"==": [{"var": "status"}, "active"]});
30//! let compiled = engine.compile(&logic).unwrap();
31//!
32//! // Evaluate with different data
33//! let data = json!({"status": "active"});
34//! let result = engine.evaluate_owned(&compiled, data).unwrap();
35//! assert_eq!(result, json!(true));
36//! ```
37//!
38//! ## Architecture
39//!
40//! The library uses a two-phase approach:
41//!
42//! 1. **Compilation**: JSON logic is parsed into `CompiledLogic` with OpCode dispatch
43//! 2. **Evaluation**: Compiled logic is evaluated against data using direct dispatch
44//!
45//! This design enables sharing compiled logic across threads and eliminates
46//! repeated parsing overhead.
47
48mod compiled;
49mod config;
50mod constants;
51mod context;
52mod datetime;
53mod engine;
54mod error;
55mod opcode;
56mod operators;
57mod trace;
58mod value_helpers;
59
60pub use compiled::{CompiledLogic, CompiledNode};
61pub use config::{
62 DivisionByZeroHandling, EvaluationConfig, NanHandling, NumericCoercionConfig, TruthyEvaluator,
63};
64pub use context::{ContextFrame, ContextStack};
65pub use engine::DataLogic;
66pub use error::Error;
67pub use opcode::OpCode;
68pub use trace::{ExecutionStep, ExpressionNode, TraceCollector, TracedResult};
69
70use serde_json::Value;
71
72/// Result type for DataLogic operations
73pub type Result<T> = std::result::Result<T, Error>;
74
75/// Trait for recursive evaluation of logic expressions.
76///
77/// This trait is implemented by the `DataLogic` engine and used internally
78/// by operators that need to recursively evaluate sub-expressions.
79///
80/// # Example
81///
82/// The `if` operator uses this trait to evaluate its condition and branches:
83///
84/// ```rust,ignore
85/// let condition_result = evaluator.evaluate(&condition, context)?;
86/// if is_truthy(&condition_result) {
87/// evaluator.evaluate(&then_branch, context)
88/// } else {
89/// evaluator.evaluate(&else_branch, context)
90/// }
91/// ```
92pub trait Evaluator {
93 /// Evaluates a logic expression within the given context.
94 ///
95 /// # Arguments
96 ///
97 /// * `logic` - The JSON logic expression to evaluate
98 /// * `context` - The context stack containing data and metadata
99 ///
100 /// # Returns
101 ///
102 /// The evaluated result as a JSON value, or an error if evaluation fails.
103 fn evaluate(&self, logic: &Value, context: &mut ContextStack) -> Result<Value>;
104}
105
106/// Trait for implementing custom operators.
107///
108/// Custom operators extend the functionality of the DataLogic engine by
109/// providing domain-specific logic. Operators must be thread-safe (`Send + Sync`).
110///
111/// # Example
112///
113/// ```rust
114/// use datalogic_rs::{Operator, ContextStack, Evaluator, Result, Error};
115/// use serde_json::{json, Value};
116///
117/// struct UpperCaseOperator;
118///
119/// impl Operator for UpperCaseOperator {
120/// fn evaluate(
121/// &self,
122/// args: &[Value],
123/// _context: &mut ContextStack,
124/// _evaluator: &dyn Evaluator,
125/// ) -> Result<Value> {
126/// if let Some(s) = args.first().and_then(|v| v.as_str()) {
127/// Ok(json!(s.to_uppercase()))
128/// } else {
129/// Err(Error::InvalidArguments("Argument must be a string".to_string()))
130/// }
131/// }
132/// }
133/// ```
134pub trait Operator: Send + Sync {
135 /// Evaluates the operator with the given arguments.
136 ///
137 /// # Arguments
138 ///
139 /// * `args` - The evaluated arguments passed to the operator
140 /// * `context` - The context stack for accessing data and metadata
141 /// * `evaluator` - The evaluator for recursive evaluation of sub-expressions
142 ///
143 /// # Returns
144 ///
145 /// The result of the operator evaluation, or an error if the operation fails.
146 fn evaluate(
147 &self,
148 args: &[Value],
149 context: &mut ContextStack,
150 evaluator: &dyn Evaluator,
151 ) -> Result<Value>;
152}