<div align="center">
<img src="https://avatars.githubusercontent.com/u/207296579?s=200&v=4" alt="Plasmatic Logo" width="120" height="120">
# datalogic-rs
**A fast, production-ready Rust engine for JSONLogic.**
Effortlessly evaluate complex rules and dynamic expressions with a powerful, memory-efficient, and developer-friendly toolkit.
[](https://opensource.org/licenses/Apache-2.0)
[](https://www.rust-lang.org)
[](https://crates.io/crates/datalogic-rs)
[](https://docs.rs/datalogic-rs)
[](https://crates.io/crates/datalogic-rs)
<p>
<a href="https://github.com/GoPlasmatic">đ˘ Organization</a> â˘
<a href="https://docs.rs/datalogic-rs">đ Docs</a> â˘
<a href="https://github.com/GoPlasmatic/datalogic-rs/issues">đ Report a Bug</a>
</p>
</div>
-----
`datalogic-rs` brings the power of [JSONLogic](http://jsonlogic.com) to Rust, focusing on speed, safety, and ease of use. Whether youâre building feature flags, dynamic pricing, or complex validation, this engine is designed to be flexible and robust.
**Whatâs New in v4?**
Weâve redesigned the API to be more ergonomic and developer-friendly. If you need maximum speed with arena allocation, v3 is still available and maintained. Choose v4 for a smoother experience, or stick with v3 for raw performanceâboth are supported.
## Key Features
- **Thread-Safe:** Compile your logic once, then evaluate it anywhereâno locks, no fuss.
- **Intuitive API:** Works seamlessly with `serde_json::Value`.
- **Fully Compliant:** Passes the official JSONLogic test suite.
- **Extensible:** Add your own operators with a simple trait.
- **Templating Support:** Preserve object structures for dynamic output.
- **Battle-Tested:** Used in production, with thorough test coverage.
- **Feature-Rich:** Over 50 built-in operators, including datetime and regex.
- **Async-Ready:** Integrates smoothly with Tokio and async runtimes.
## Getting Started
### 1. Basic Usage
```rust
use datalogic_rs::DataLogic;
use serde_json::json;
let engine = DataLogic::new();
let logic = json!({ ">": [{ "var": "age" }, 18] });
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({ "age": 21 })).unwrap();
assert_eq!(result, json!(true));
```
### 2. Quick JSON Evaluation
```rust
use datalogic_rs::DataLogic;
use serde_json::json;
let engine = DataLogic::new();
let result = engine.evaluate_json(
r#"{ "+": [1, 2] }"#,
r#"{}"#
).unwrap();
assert_eq!(result, json!(3));
```
### 3. Thread-Safe Evaluation
```rust
use datalogic_rs::DataLogic;
use serde_json::json;
use std::sync::Arc;
let engine = Arc::new(DataLogic::new());
let logic = json!({ "*": [{ "var": "x" }, 2] });
let compiled = engine.compile(&logic).unwrap();
// Share across threads
let engine2 = Arc::clone(&engine);
let compiled2 = Arc::clone(&compiled);
});
```
## Installation
Add this to your `Cargo.toml`:
```toml
[dependencies]
datalogic-rs = "4.0"
# Or use v3 if you need arena-based allocation for maximum performance
# datalogic-rs = "3.0"
```
## Examples
### Conditional Logic
```rust
use datalogic_rs::DataLogic;
use serde_json::json;
let engine = DataLogic::new();
let logic = json!({
"if": [
{ ">=": [{ "var": "age" }, 18] },
"adult",
"minor"
]
});
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({ "age": 25 })).unwrap();
assert_eq!(result, json!("adult"));
```
### Array Operations
```rust
use datalogic_rs::DataLogic;
use serde_json::json;
let engine = DataLogic::new();
let logic = json!({
"filter": [
[1, 2, 3, 4, 5],
{ ">": [{ "var": "" }, 2] }
]
});
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({})).unwrap();
assert_eq!(result, json!([3, 4, 5]));
```
### String Operations
```rust
use datalogic_rs::DataLogic;
use serde_json::json;
let engine = DataLogic::new();
let logic = json!({ "cat": ["Hello, ", { "var": "name" }, "!"] });
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({ "name": "World" })).unwrap();
assert_eq!(result, json!("Hello, World!"));
```
### Math Operations
```rust
use datalogic_rs::DataLogic;
use serde_json::json;
let engine = DataLogic::new();
let logic = json!({ "max": [{ "var": "scores" }] });
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({ "scores": [10, 20, 15] })).unwrap();
assert_eq!(result, json!(20));
```
## Custom Operators
Extend the engine with your own logic:
```rust
use datalogic_rs::{DataLogic, Operator, ContextStack, Evaluator, Result, Error};
use serde_json::{json, Value};
struct DoubleOperator;
impl Operator for DoubleOperator {
fn evaluate(
&self,
args: &[Value],
_context: &mut ContextStack,
_evaluator: &dyn Evaluator,
) -> Result<Value> {
match args.first().and_then(|v| v.as_f64()) {
Some(n) => Ok(json!(n * 2.0)),
None => Err(Error::InvalidArguments("Expected number".to_string()))
}
}
}
let mut engine = DataLogic::new();
engine.add_operator("double".to_string(), Box::new(DoubleOperator));
let result = engine.evaluate_json(r#"{ "double": 21 }"#, r#"{}"#).unwrap();
assert_eq!(result, json!(42.0));
```
## Advanced Features
### Nested Data Access
```rust
use datalogic_rs::DataLogic;
use serde_json::json;
let engine = DataLogic::new();
let logic = json!({ "var": "user.address.city" });
let data = json!({
"user": {
"address": {
"city": "New York"
}
}
});
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, data).unwrap();
assert_eq!(result, json!("New York"));
```
### Error Handling
```rust
let logic = json!({
"try": [
{ "/": [10, { "var": "divisor" }] },
0 // Default value on error
]
});
```
## Async Support
Works seamlessly with async runtimes:
```rust
use datalogic_rs::DataLogic;
use serde_json::json;
use std::sync::Arc;
#[tokio::main]
async fn main() {
let engine = Arc::new(DataLogic::new());
let logic = json!({ "*": [{ "var": "x" }, 2] });
let compiled = engine.compile(&logic).unwrap();
let handle = tokio::task::spawn_blocking(move || {
engine.evaluate_owned(&compiled, json!({ "x": 5 }))
});
let result = handle.await.unwrap().unwrap();
assert_eq!(result, json!(10));
}
```
## Use Cases
### Feature Flags
```json
{
"and": [
{ "==": [{ "var": "user.country" }, "US"] },
{ "or": [
{ "==": [{ "var": "user.role" }, "beta_tester"] },
{ ">=": [{ "var": "user.account_age_days" }, 30] }
] }
]
}
```
### Dynamic Pricing
```json
{
"if": [
{ ">=": [{ "var": "cart.total" }, 100] },
{ "-": [{ "var": "cart.total" }, { "*": [{ "var": "cart.total" }, 0.1] }] },
{ "var": "cart.total" }
]
}
```
### Fraud Detection
```json
{
"or": [
{ "and": [
{ "!=": [{ "var": "transaction.billing_country" }, { "var": "user.country" }] },
{ ">=": [{ "var": "transaction.amount" }, 1000] }
] },
{ ">=": [{ "var": "transaction.attempts_last_hour" }, 5] }
]
}
```
## Supported Operators
Over 50 built-in operators, including:
| **Comparison** | `==`, `===`, `!=`, `!==`, `>`, `>=`, `<`, `<=` |
| **Logic** | `and`, `or`, `!`, `!!` |
| **Arithmetic** | `+`, `-`, `*`, `/`, `%`, `min`, `max`, `abs`, `ceil`, `floor` |
| **Control Flow** | `if`, `?:` (ternary), `??` (coalesce) |
| **Arrays** | `map`, `filter`, `reduce`, `all`, `some`, `none`, `merge`, `in`, `length`, `slice`, `sort` |
| **Strings** | `cat`, `substr`, `starts_with`, `ends_with`, `upper`, `lower`, `trim`, `split` |
| **Data Access** | `var`, `val`, `exists`, `missing`, `missing_some` |
| **DateTime** | `datetime`, `timestamp`, `now`, `parse_date`, `format_date`, `date_diff` |
| **Type** | `type` (returns type name as string) |
| **Error Handling**| `throw`, `try` |
| **Special** | `preserve` (for structured object preservation) |
| **Custom** | User-defined operators via `Operator` trait |
## Architecture
- **Compilation:** Parses and optimizes logic for fast evaluation.
- **Evaluation:** Uses OpCode dispatch and context stack for speed.
- **Thread-Safe:** Share compiled logic with zero-copy via Arc.
1. **Compilation Phase:** JSON logic is parsed and compiled into a `CompiledLogic` structure with:
- Static evaluation of constant expressions
- OpCode assignment for built-in operators
- Thread-safe Arc wrapping for sharing across threads
2. **Evaluation Phase:** The compiled logic is evaluated against data with:
- Direct OpCode dispatch (avoiding string lookups)
- Context stack for nested evaluations
- Zero-copy operations where possible
## Performance Optimizations
- OpCode dispatch for built-in operators
- Static evaluation of constant expressions
- SmallVec for small arrays
- Arc sharing for thread safety
- Cow types for efficient value passing
## About Plasmatic
Created by [Plasmatic](https://github.com/GoPlasmatic), building open-source tools for financial infrastructure and data processing.
Check out our other projects:
- [DataFlow-rs](https://github.com/GoPlasmatic/dataflow-rs): Event-driven workflow orchestration in Rust.
## License
Licensed under Apache 2.0. See [LICENSE](LICENSE) for details.