Migration Guide
This guide covers migrating between major versions of datalogic-rs.
v3 to v4 Migration
Overview
v4 redesigns the API for ergonomics and simplicity. The core JSONLogic behavior is unchanged, but the Rust API is different.
Key changes:
- Simplified
DataLogicengine API CompiledLogicautomatically wrapped inArc- No more arena allocation (simpler lifetime management)
- New evaluation methods
When to Migrate
Migrate to v4 if:
- Starting a new project
- Want simpler, more ergonomic API
- Don’t need arena-based memory optimization
- Want easier thread safety
Stay on v3 if:
- Already using v3 in production with no issues
- Need maximum performance with arena allocation
- Have complex lifetime requirements
API Changes
Engine Creation
#![allow(unused)]
fn main() {
// v3
use datalogic_rs::DataLogic;
let engine = DataLogic::default();
// v4
use datalogic_rs::DataLogic;
let engine = DataLogic::new();
// v4 with config
use datalogic_rs::{DataLogic, EvaluationConfig};
let engine = DataLogic::with_config(EvaluationConfig::default());
}
Compilation
#![allow(unused)]
fn main() {
// v3
let compiled = engine.compile(&logic)?;
// compiled is not automatically Arc-wrapped
// v4
let compiled = engine.compile(&logic)?;
// compiled is Arc<CompiledLogic>, thread-safe by default
}
Evaluation
#![allow(unused)]
fn main() {
// v3
let result = engine.evaluate(&compiled, &data)?;
// v4 - two options
// Option 1: Takes owned data, returns Value
let result = engine.evaluate_owned(&compiled, data)?;
// Option 2: Takes reference, returns Cow<Value>
let result = engine.evaluate(&compiled, &data)?;
}
Quick Evaluation
#![allow(unused)]
fn main() {
// v3
let result = engine.apply(&logic, &data)?;
// v4
let result = engine.evaluate_json(
r#"{"==": [1, 1]}"#,
r#"{}"#
)?;
}
Custom Operators
#![allow(unused)]
fn main() {
// v3
struct MyOperator;
impl Operator for MyOperator {
fn evaluate(&self, args: &[Value], data: &Value, engine: &DataLogic) -> Result<Value> {
// ...
}
}
// v4
use datalogic_rs::{Operator, ContextStack, Evaluator, Result};
struct MyOperator;
impl Operator for MyOperator {
fn evaluate(
&self,
args: &[Value],
context: &mut ContextStack,
evaluator: &dyn Evaluator,
) -> Result<Value> {
// Arguments are unevaluated - call evaluator.evaluate() as needed
let value = evaluator.evaluate(&args[0], context)?;
// ...
}
}
}
Thread Safety
#![allow(unused)]
fn main() {
// v3 - Manual Arc wrapping
use std::sync::Arc;
let compiled = engine.compile(&logic)?;
let compiled_arc = Arc::new(compiled);
// v4 - Already Arc-wrapped
let compiled = engine.compile(&logic)?; // Already Arc<CompiledLogic>
let compiled_clone = Arc::clone(&compiled);
}
Configuration Changes
#![allow(unused)]
fn main() {
// v3
let engine = DataLogic::default();
// v4
use datalogic_rs::{DataLogic, EvaluationConfig, NanHandling};
let config = EvaluationConfig::default()
.with_nan_handling(NanHandling::IgnoreValue);
let engine = DataLogic::with_config(config);
}
Structured Objects
#![allow(unused)]
fn main() {
// v3
let engine = DataLogic::with_preserve_structure(true);
// v4
let engine = DataLogic::with_preserve_structure();
// v4 with config
let config = EvaluationConfig::default();
let engine = DataLogic::with_config_and_structure(config, true);
}
Error Handling
#![allow(unused)]
fn main() {
// v3
use datalogic_rs::Error;
match engine.evaluate(&compiled, &data) {
Ok(result) => { /* ... */ }
Err(Error::UnknownOperator(op)) => { /* ... */ }
Err(e) => { /* ... */ }
}
// v4 - Same pattern
use datalogic_rs::Error;
match engine.evaluate_owned(&compiled, data) {
Ok(result) => { /* ... */ }
Err(Error::UnknownOperator(op)) => { /* ... */ }
Err(e) => { /* ... */ }
}
}
Migration Checklist
-
Update Cargo.toml:
[dependencies] datalogic-rs = "4.0" -
Update engine creation:
DataLogic::default()→DataLogic::new()
-
Update evaluation calls:
engine.evaluate(&compiled, &data)→engine.evaluate_owned(&compiled, data.clone())- Or use
engine.evaluate(&compiled, &data)for reference-based evaluation
-
Update custom operators:
- Add
context: &mut ContextStackparameter - Replace
engine: &DataLogicwithevaluator: &dyn Evaluator - Call
evaluator.evaluate()on arguments
- Add
-
Remove manual Arc wrapping:
CompiledLogicis now automaticallyArc<CompiledLogic>
-
Test thoroughly:
- Run your test suite
- Verify expected behavior with your specific rules
Performance Considerations
v4 trades some raw performance for a simpler API:
- No arena allocation means more heap allocations
Arcwrapping adds a small overhead for single-threaded use- For most use cases, the difference is negligible
If you need maximum performance:
- Reuse
CompiledLogicinstances - Use
evaluatewith references for large data - Consider staying on v3 for hot paths
Getting Help
If you encounter issues during migration:
- Check the API Reference
- Review the examples
- Open an issue on GitHub