Skip to content

Repository files navigation

QlExpress Rust

An embeddable Rust expression and dynamic scripting engine, behaviorally ported from Alibaba QLExpress4.

Crates.io docs.rs Production Readiness MSRV License

English | 简体中文

Quick start · Capabilities · Architecture · Compatibility · Verification · Documentation


Release: 0.1.0
Maturity: first stable release; SemVer guarantees apply
Java baseline: QLExpress4 4.2.0-beta, commit 9065b9ac5d985dcd02e627239aa9cdb78fb2f7f3
Last verified: 2026-09-05

QlExpress Rust evaluates expressions and rule scripts inside a Rust process. It provides a parser, a stack-based QVM, Java-compatible value and error semantics, custom functions and operators, explicit native-type registration, security policies, compile caches, and expression tracing.

The repository has passed its repeatable local and CI readiness gates. That is evidence for the library and its harness—not proof that an arbitrary business deployment is production-ready. Real scripts, data, capacity limits, monitoring, rollout, and rollback must still be accepted in each host environment.

Why QlExpress Rust?

  • Embed business rules without starting a separate service or JVM.
  • Use a familiar C/Java-like expression language with lists, maps, lambdas, functions, loops, dynamic strings, macros, and structured errors.
  • Extend the language through Rust closures, custom operators, and registered host types.
  • Keep host access explicit: Rust uses NativeRegistry instead of unrestricted JVM reflection.
  • Compare behavior against a pinned QLExpress4 baseline with differential and replay tests.

Good fit

  • pricing, promotion, eligibility, routing, scoring, and validation rules;
  • configurable expressions embedded in a Rust application;
  • teams migrating QLExpress4 rule behavior from Java to Rust.

Boundaries

  • This is not a Java ABI/JVM replacement.
  • A single Express4Runner is not Send or Sync; use one runner per worker thread.
  • Rust native methods and constructors are registered explicitly; derive cannot inspect impl blocks.
  • 0.1.0 is the first stable release; standard SemVer guarantees apply.

Architecture

script + host context + QLOptions
                 │
                 ▼
┌──────────────────────────────────────────────────────────────┐
│ Express4Runner                                               │
│  lexer/parser → syntax tree → instruction compiler           │
│       │                              │                       │
│       └──────── compile cache ◄──────┘                       │
│                                      ▼                       │
│  functions / operators / NativeRegistry → QVM + QLambda     │
│                                      │                       │
│                       result / trace / structured error       │
└──────────────────────────────────────────────────────────────┘

The execution path is:

Express4Runner::execute
  → execute_with_context
  → parse_to_definition_with_cache | parse_definition
  → parse_to_syntax_tree
  → QvmInstructionVisitor::compile
  → QvmRuntime::execute
  → QLambdaInner / run_instructions
  → QLResult
Crate Published Responsibility
qlexpress Yes Public facade, parser, compiler, QVM, values, extensions, security
qlexpress-derive Yes #[derive(QLExpressType)] for registered host structs
qlexpress-verification No Differential, replay, concurrency, load, fuzz, host, and canary harness

See Architecture for component boundaries, runtime flows, security, failure handling, and architecture decisions.

Capabilities

Capability Status Evidence / limit
Expressions, control flow, functions, lambdas, lists, maps Implemented Alignment and stage integration tests
Custom functions, operators, aliases, and macros Implemented Public Express4Runner APIs
Structured syntax/runtime/timeout errors Implemented Stable error codes and source positions
Parse-cache export/import Implemented JSON model v1 and round-trip tests
Expression tracing Implemented Compile-time trace points plus runtime collection
Host-type derive Implemented Fields, aliases, skip, name override; no generic structs
Native methods and constructors Explicit registration Not discovered from Rust impl blocks
Security policy Implemented Isolation default; open, allowlist, and denylist modes
Checked sandbox execution Implemented Finite budgets, unified capabilities, tenant LRU, cancellation
Hard process isolation Available Supervised one-shot worker; Linux OS memory limit
Multi-thread execution Runner per worker Sharing one runner across threads is unsupported
Cross-platform support Not yet claimed CI currently executes on Ubuntu only

Quick start

Requirements

  • Rust 1.85 or newer
  • Cargo with Rust Edition 2021 support

Add the crate:

cargo add qlexpress@0.1.0

Evaluate a script:

use std::collections::HashMap;

use qlexpress::{DataValue, Express4Runner, QLOptions};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let runner = Express4Runner::new();
    let options = QLOptions::builder().cache(true).build();

    let mut context = HashMap::new();
    context.insert("price".to_string(), DataValue::Double(125.0));
    context.insert("vip".to_string(), DataValue::Bool(true));

    let result = runner.execute(
        "vip ? price * 0.8 : price",
        context,
        &options,
    )?;

    assert_eq!(result.into_result(), DataValue::Double(100.0));
    Ok(())
}

Run the repository example:

cargo run -p qlexpress --example quick_start

Expected output:

100.0

Common extensions

Register a Rust function:

use qlexpress::DataValue;

runner.add_varargs_function("sumAll", |values: &[DataValue]| {
    let total = values.iter().filter_map(|value| match value {
        DataValue::Int(value) => Some(*value),
        _ => None,
    }).sum();
    Ok(DataValue::Int(total))
});

Expose a host struct:

use qlexpress::{QLExpressType, QLSecurityStrategy};

#[derive(QLExpressType)]
#[qlexpress(name = "com.example.Order")]
struct Order {
    id: String,
    amount: f64,
    #[qlexpress(skip)]
    internal_note: String,
}

let mut runner = Express4Runner::with_init_options(
    qlexpress::InitOptions::builder()
        .security_strategy(QLSecurityStrategy::open())
        .build(),
);
runner.register_qlexpress_type::<Order>();

The open policy is shown to make the example explicit. Prefer isolation or a narrow allowlist for untrusted scripts. The full examples and limits are in the Usage Guide.

Java compatibility

The behavioral authority is Alibaba QLExpress4 4.2.0-beta at commit 9065b9ac. Compatibility is verified through upstream script replay, a shared differential corpus, object/semantic matrices, and Rust-native integration tests.

Java design Rust design Compatibility intent
Express4Runner Express4Runner Facade and execution behavior
ANTLR syntax tree + visitor Rust lexer/parser + visitor compiler Script behavior, not parser implementation identity
JVM reflection ReflectLoader + NativeRegistry Explicit, security-checked host integration
Exceptions Result<T, QLException> Error category, code, location, and reason
ConcurrentHashMap<String, Future<...>> RefCell<HashMap<...>> Cache-hit semantics in a single-thread runner
Java annotations #[derive(QLExpressType)] + explicit registration Compile-time field metadata; no runtime method scanning
Dynamic proxies Explicit closure/trait adapters Idiomatic Rust replacement

Detailed mappings:

Concurrency and security

Express4Runner owns Rc/RefCell state. Create and configure one runner per worker thread, then reuse it inside that worker to benefit from compile caching. Do not wrap one runner in a mutex and assume Java-equivalent concurrency.

The default native-member policy is QLSecurityStrategy::Isolation. Plain execute preserves Java-compatible unlimited defaults and is not an untrusted-input sandbox. Untrusted scripts must use execute_checked or the supervised process worker. See the Security Sandbox for budgets, capabilities, cancellation, OS limits, and residual boundaries.

Versioning

0.1.0 is the first stable release with standard SemVer guarantees (1.0+ compatibility discipline). Pinning the exact version is still recommended for reproducible builds:

[dependencies]
qlexpress = "0.1.0"
qlexpress-derive = "0.1.0"

Or with the CLI:

cargo add qlexpress@0.1.0
cargo add qlexpress-derive@0.1.0

The = prefix forces an exact version match. Both qlexpress and qlexpress-derive must use the same version — they are published in lockstep and mixing versions will produce compile errors.

Before upgrading, review CHANGELOG.md for breaking changes. 0.1.0 already carries standard semver guarantees; breaking changes require a major version bump.

Verification

The current repository gates include:

cargo fmt --all -- --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace --all-features
RUSTDOCFLAGS="-D warnings" cargo doc --workspace --all-features --no-deps

Production-readiness CI additionally runs the pinned Java suite, Java/Rust differential tests, official script replay, runner-per-worker concurrency, deterministic security fuzzing, a business-host scenario, canary/rollback simulation, load acceptance, and libFuzzer.

The current strict inventory records 1,118 Rust test functions and passes the full workspace, 293/293 current live Java/Rust differential cases (plus 2 documented intentional Java-baseline divergences), 225 Maven tests, and 151/151 independent Java resource-script replays. Differential parity—not Rust code coverage—is the semantic acceptance criterion. The extended production run also records 228/228 Java official-suite cases, 16,000 concurrent executions, a 60-second soak, 25,000 deterministic security cases, and a 31-second libFuzzer run. Read the commands, measurements, and remaining deployment boundary in Production Acceptance.

Documentation

Document English 简体中文
Migration technical requirements Technical Requirements 技术要求
Migration test ledgers Test Ledgers 迁移测试对照表
Project overview README README
Usage guide Usage Guide 使用指南
Architecture Architecture 架构文档
API reference docs.rs Source rustdoc includes bilingual notes
Production acceptance 生产验收
Security sandbox Security Sandbox 安全沙箱

Development and release

Development happens on dev; main is the release branch. A v* tag contained in main runs the complete readiness workflow before publishing qlexpress-derive and then qlexpress.

cargo build --workspace
cargo test --workspace --all-features
cargo publish -p qlexpress-derive --dry-run
cargo publish -p qlexpress --dry-run

Do not publish qlexpress before the matching exact version of qlexpress-derive is available.

License

Licensed under the Apache License, Version 2.0. QLExpress is an Alibaba project; this Rust port is maintained independently by the easy-4-rust organization.


About

Embeddable Rust expression and dynamic scripting engine, behaviorally ported from Alibaba QLExpress4.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages