Skip to content

copyleftdev/seuil-rs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

seuil

A complete, safe JSONata implementation in Rust — JSON query, transform, and expression evaluation.

CI Crates.io Docs.rs License MSRV unsafe forbidden

Documentation | Crate | Repository | Changelog


Seuil (French: "threshold") is a JSONata query and transformation engine for Rust. Compile a JSONata expression once, then evaluate it against any JSON input — fast, safe, and spec-compliant.

use seuil::Seuil;

let expr = Seuil::compile("orders[status='paid'].amount ~> $sum()")?;

let data = serde_json::json!({
    "orders": [
        {"status": "paid", "amount": 100},
        {"status": "pending", "amount": 50},
        {"status": "paid", "amount": 200}
    ]
});

let result = expr.evaluate(&data)?;
assert_eq!(result, serde_json::json!(300.0));

Why seuil?

  • 99.6% JSONata spec compliance — 1,027 of 1,031 official test cases pass
  • Zero unsafe#![forbid(unsafe_code)] enforced at the crate level
  • Zero panics on any input — malformed expressions and data return Err, never crash
  • Compile once, evaluate many — parse the expression once, run it against thousands of inputs
  • Fast-path JSON inputserde_json::Value converts directly to the arena, bypassing the expression parser
  • 47 built-in functions — strings, math, arrays, objects, higher-order functions, date/time, regex
  • Arena allocation — all values in a bumpalo arena for cache-friendly, zero-fragmentation evaluation
  • Deterministic simulation testing — injectable Environment trait for fully reproducible evaluation

Quick Start

Add to your Cargo.toml:

[dependencies]
seuil = "0.1"
serde_json = "1"

API

use seuil::{Seuil, EvalConfig};

// Compile a JSONata expression
let expr = Seuil::compile("$.name")?;

// Evaluate against serde_json::Value
let result = expr.evaluate(&serde_json::json!({"name": "Alice"}))?;

// Evaluate against a JSON string
let result = expr.evaluate_str(r#"{"name": "Bob"}"#)?;

// Evaluate with no input
let result = Seuil::compile("1 + 2")?.evaluate_empty()?;

// Evaluate with custom config (timeouts, depth limits)
let config = EvalConfig {
    max_depth: Some(100),
    time_limit_ms: Some(1000),
    ..Default::default()
};
let result = expr.evaluate_with_config(&serde_json::json!(null), &config)?;

Built-in Functions

String

$string · $length · $substring · $substringBefore · $substringAfter · $uppercase · $lowercase · $trim · $pad · $contains · $split · $join · $replace · $match · $base64encode · $base64decode

Numeric

$number · $abs · $floor · $ceil · $round · $power · $sqrt · $random · $sum · $max · $min · $average

Array

$count · $append · $sort · $reverse · $shuffle · $distinct · $zip · $flatten

Object

$keys · $lookup · $spread · $merge · $sift · $each · $error · $assert · $type

Higher-Order

$map · $filter · $single · $reduce

Type & Logic

$boolean · $not · $exists

Date/Time

$now · $millis · $fromMillis · $toMillis

Deterministic Testing

All non-determinism ($now(), $millis(), $random(), $uuid()) is injectable via the Environment trait:

use seuil::clock::MockEnvironment;
use seuil::EvalConfig;

let env = MockEnvironment::new(0xDEAD_BEEF);
let config = EvalConfig::with_environment(&env);
// Every evaluation with the same seed produces identical results.

Comparison

Feature seuil jsonata-rs (Stedi) jsonpath-rust jmespath
Language JSONata JSONata JSONPath JMESPath
Spec compliance 99.6% ~68% (self-described "incomplete") N/A N/A
Unsafe code forbid(unsafe_code) 4 blocks (incl. UB) Uses unsafe No unsafe
Functions 47 built-in ~35 (14+ missing) None 50+
Higher-order functions Full ($map, $filter, $reduce) Stubs for some No Limited
Expression compilation Compile once, eval many Per-evaluation parse Per-evaluation Compile + eval
Deterministic testing MockEnvironment None None None
Fuzz testing libfuzzer + VOPR + chaos None None None
Arena allocation bumpalo bumpalo No No

Testing Rigor

seuil is tested beyond typical crate standards:

  • Official test suite: 1,027 of 1,031 active JSONata test cases pass
  • VOPR campaigns: 10,000+ seed verification with zero panics
  • Chaos testing: 9 fault injection categories — truncation, deep nesting, huge arrays, Unicode stress, type confusion, malformed input, resource exhaustion
  • Property-based testing: ~14,000 generated cases via proptest
  • Coverage-guided fuzzing: libfuzzer with corpus and dictionary
  • Continuous verification: GitHub Actions CI on every push, nightly fuzzing and VOPR campaigns

Use Cases

seuil was built for dental RPA and healthcare EDI processing at Zuub, where expressions evaluate thousands of eligibility responses, claims, and benefit structures:

use seuil::Seuil;

// Extract dental benefits from an eligibility response
let expr = Seuil::compile("benefitInformation[serviceType='35' and code='1'].amount")?;
let max = expr.evaluate(&eligibility_response)?;

// Get patient name
let name = Seuil::compile("subscriber.firstName & ' ' & subscriber.lastName")?;
let full_name = name.evaluate(&eligibility_response)?;

It works equally well for any JSON query and transformation task — API response processing, configuration extraction, data pipeline transforms, log analysis, and more.

Architecture

Expression String ──→ Parser (Pratt) ──→ AST ──→ Evaluator ──→ Result
                                                     │
                                          ┌──────────┴──────────┐
                                          │  bumpalo Arena       │
                                          │  (all values here)   │
                                          │  ScopeStack          │
                                          │  47 native functions │
                                          │  Environment trait   │
                                          └─────────────────────┘
  • Parser: Pratt parser with operator precedence, all JSONata operators including parent (%)
  • Evaluator: Recursive AST walker with tail-call optimization trampoline
  • Values: Arena-allocated via bumpalo — zero per-value heap allocation, cache-friendly layout
  • Scope: Stack-based variable scoping with lambda capture snapshots (no Rc<RefCell>)
  • Functions: 47 built-in functions with higher-order function callback wiring

Minimum Supported Rust Version

The MSRV is 1.77.0.

License

Licensed under the Apache License, Version 2.0.

Contributing

See CONTRIBUTING for development setup, running tests, benchmarks, VOPR campaigns, and the fuzzer.