A high-performance, production-ready Rust implementation of JSONLogic.
Evaluate complex rules and dynamic expressions with a powerful, memory-efficient, and developer-friendly engine.
datalogic-rs
is a Rust implementation of JSONLogic designed for performance, safety, and ease of use. Whether you're building a feature flagging system, a dynamic pricing engine, or a complex validation pipeline, datalogic-rs
provides the power and flexibility you need.
Our secret sauce? An arena-based memory model that delivers zero-copy parsing and evaluation, making it up to 30% faster than other implementations.
- Blazing Fast: Our arena-based allocator minimizes memory overhead and maximizes speed.
- 100% JSONLogic Compliant: Full compatibility with the official JSONLogic test suite.
- Extensible: Easily add your own custom operators with our simple or advanced APIs.
- Powerful Templating: Preserve object structures for dynamic, structured output.
- Ready for Production: Thread-safe, statically dispatched, and optimized for real-world workloads.
- Batteries Included: Over 50 built-in operators, including datetime and regex support.
- WASM Support: Compile to WebAssembly for use in browser environments.
datalogic-rs
uses an arena allocator for memory-efficient evaluation. This means we can parse a rule once and evaluate it many times with minimal overhead.
use datalogic_rs::DataLogic;
// Create an evaluator with a custom arena chunk size.
let dl = DataLogic::with_chunk_size(8192);
// Parse your rule and data once.
let rule = dl.parse_logic(r#"{ ">": [{ "var": "temp" }, 100] }"#).unwrap();
let data = dl.parse_data(r#"{ "temp": 110 }"#).unwrap();
// Evaluate efficiently.
let result = dl.evaluate(&rule, &data).unwrap();
assert!(result.to_json().as_bool().unwrap());
// Reset the arena to reuse memory for the next batch of evaluations.
dl.reset_arena();
Generate powerful, structured outputs by preserving non-operator keys in your JSON.
use datalogic_rs::DataLogic;
// Enable structure preservation for templating.
let dl = DataLogic::with_preserve_structure();
let result = dl.evaluate_str(
r#"{
"status": { "if": [{ ">=": [{ "var": "score" }, 90] }, "pass", "fail"] },
"grade": { "+": [{ "var": "score" }, { "var": "bonus" }] },
"timestamp": { "now": [] }
}"#,
r#"{ "score": 85, "bonus": 10 }"#,
).unwrap();
// The result is a structured object with evaluated fields.
// { "status": "pass", "grade": 95, "timestamp": "2024-01-15T10:30:00Z" }
Ideal for scenarios where you need to run the same rule against different data.
use datalogic_rs::DataLogic;
let dl = DataLogic::new();
// Parse the rule and data once.
let rule = dl.parse_logic(r#"{ ">": [{ "var": "temp" }, 100] }"#).unwrap();
let data = dl.parse_data(r#"{ "temp": 110 }"#).unwrap();
// Evaluate as many times as you need.
let result = dl.evaluate(&rule, &data).unwrap();
assert!(result.to_json().as_bool().unwrap());
Perfect for when your rules are dynamic or you only need a single evaluation.
use datalogic_rs::DataLogic;
let dl = DataLogic::new();
let result = dl.evaluate_str(
r#"{ "abs": -42 }"#,
r#"{}"#, // No data needed for this rule.
).unwrap();
assert_eq!(result.as_i64().unwrap(), 42);
Works directly with serde_json::Value
for easy integration into your existing JSON workflows.
use datalogic_rs::DataLogic;
use serde_json::json;
let dl = DataLogic::new();
let logic = json!({
"if": [
{ ">": [{ "var": "cart.total" }, 100] },
"Premium",
"Standard"
]
});
let data = json!({ "cart": { "total": 120 } });
let result = dl.evaluate_json(&logic, &data).unwrap();
assert_eq!(result.as_str().unwrap(), "Premium");
Add datalogic-rs
to your Cargo.toml
:
[dependencies]
datalogic-rs = "3.0"
Check for multi-condition eligibility with nested logic.
use datalogic_rs::DataLogic;
let dl = DataLogic::new();
let result = dl.evaluate_str(
r#"{
"and": [
{ ">=": [{ "var": "age" }, 18] },
{ "<": [{ "var": "age" }, 65] },
{ "or": [
{ "==": [{ "var": "subscription" }, "premium"] },
{ ">=": [{ "var": "purchases" }, 5] }
] }
]
}"#,
r#"{ "age": 25, "subscription": "basic", "purchases": 7 }"#,
).unwrap();
assert!(result.as_bool().unwrap());
Filter and map user data with ease.
use datalogic_rs::DataLogic;
let dl = DataLogic::new();
let result = dl.evaluate_str(
r#"{
"map": [
{
"filter": [
{ "var": "users" },
{ ">=": [{ "var": "age" }, 18] }
]
},
{ "var": "name" }
]
}"#,
r#"{
"users": [
{ "name": "Alice", "age": 20 },
{ "name": "Bob", "age": 15 },
{ "name": "Charlie", "age": 25 }
]
}"#,
).unwrap();
// Returns ["Alice", "Charlie"]
assert_eq!(result.as_array().unwrap().len(), 2);
Perform date arithmetic with timezone support.
use datalogic_rs::DataLogic;
let dl = DataLogic::new();
let result = dl.evaluate_str(
r#"{
">": [
{ "+": [
{ "datetime": "2023-07-15T08:30:00Z" },
{ "duration": "2d" }
] },
{ "datetime": "2023-07-16T08:30:00Z" }
]
}"#,
r#"{}"#,
).unwrap();
assert!(result.as_bool().unwrap());
Extract structured data from strings using named capture groups.
use datalogic_rs::DataLogic;
let dl = DataLogic::new();
let result = dl.evaluate_str(
r#"{
"split": [
"SBININBB101",
"^(?P<bank>[A-Z]{4})(?P<country>[A-Z]{2})(?P<location>[A-Z0-9]{2})(?P<branch>[A-Z0-9]{3})?$"
]
}"#,
r#"{}"#,
).unwrap();
// Returns: { "bank": "SBIN", "country": "IN", "location": "BB", "branch": "101" }
let obj = result.as_object().unwrap();
assert_eq!(obj.get("bank").unwrap().as_str().unwrap(), "SBIN");
Extend datalogic-rs
with your own domain-specific logic.
use datalogic_rs::{DataLogic, DataValue};
use datalogic_rs::value::NumberValue;
// Define a custom operator that doubles a number.
fn double(args: Vec<DataValue>, _data: DataValue) -> Result<DataValue, String> {
if let Some(n) = args.first().and_then(|v| v.as_f64()) {
return Ok(DataValue::Number(NumberValue::from_f64(n * 2.0)));
}
Err("Argument must be a number".to_string())
}
let mut dl = DataLogic::new();
dl.register_simple_operator("double", double);
// Use your custom operator in a rule.
let result = dl.evaluate_str(r#"{ "double": 21 }"#, r#"{}"#).unwrap();
assert_eq!(result.as_f64().unwrap(), 42.0);
datalogic-rs
is fast. Here's how it stacks up against other implementations on an Apple M2 Pro:
Implementation | Execution Time | Relative Performance |
---|---|---|
datalogic-rs |
380ms | 1.0x (baseline) |
json-logic-engine (pre-compiled) |
417ms | 1.1x slower |
json-logic-engine (interpreted) |
986ms | 2.6x slower |
json-logic-js |
5,755ms | 15.1x slower |
Benchmarks run on the standard JSONLogic test suite.
{
"and": [
{ "==": [{ "var": "user.country" }, "US"] },
{ "or": [
{ "==": [{ "var": "user.role" }, "beta_tester"] },
{ ">=": [{ "var": "user.account_age_days" }, 30] }
] }
]
}
{
"if": [
{ ">=": [{ "var": "cart.total" }, 100] },
{ "-": [{ "var": "cart.total" }, { "*": [{ "var": "cart.total" }, 0.1] }] },
{ "var": "cart.total" }
]
}
{
"or": [
{ "and": [
{ "!=": [{ "var": "transaction.billing_country" }, { "var": "user.country" }] },
{ ">=": [{ "var": "transaction.amount" }, 1000] }
] },
{ ">=": [{ "var": "transaction.attempts_last_hour" }, 5] }
]
}
Category | Operators |
---|---|
Comparison | == , === , != , !== , > , >= , < , <= |
Logic | and , or , ! , !! |
Arithmetic | + , - , * , / , % , min , max , abs , ceil , floor |
Control Flow | if , ?: , ?? |
Arrays | map , filter , reduce , all , some , none , merge , in , length , slice , sort |
Strings | cat , substr , starts_with , ends_with , upper , lower , trim , replace , split |
Data Access | var , val , exists , missing , missing_some |
DateTime | datetime , timestamp , now , parse_date , format_date , date_diff |
Error Handling | throw , try |
Custom | User-defined operators |
We welcome contributions! Please see CONTRIBUTING.md for details on how to get started.
datalogic-rs
is developed by Plasmatic, an organization dedicated to building open-source tools for financial infrastructure and data processing.
Check out our other projects:
- SwiftMTMessage: A library for parsing SWIFT MT messages with CBPR+ compliance.
- Reframe: A transformation engine for converting SWIFT MT messages to ISO 20022.
- MXMessage: A library for parsing ISO 20022 (MX) messages.
datalogic-rs
is licensed under the Apache License, Version 2.0. See LICENSE for details.
Built with β€οΈ by the Plasmatic team