Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Configuration

Customize evaluation behavior with EvaluationConfig and the EngineBuilder.

Creating a Configured Engine

use datalogic_rs::{Engine, EvaluationConfig, NanHandling};

// Default configuration
let engine = Engine::new();

// Custom configuration
let config = EvaluationConfig::default()
    .with_arithmetic_nan_handling(NanHandling::IgnoreValue);
let engine = Engine::builder().with_config(config).build();

v5 dropped the inherent Engine::with_config / with_preserve_structure / with_config_and_structure constructors — use the builder. There is no compatibility shim. See the Migration Guide for the v4 → v5 mapping.

Configuration Options

EvaluationConfig is #[non_exhaustive]. Construct it with default() (or a preset such as safe_arithmetic() / strict()), then chain the with_* setters:

use datalogic_rs::{EvaluationConfig, NanHandling, DivisionByZeroHandling};

let config = EvaluationConfig::default()
    .with_arithmetic_nan_handling(NanHandling::IgnoreValue)
    .with_division_by_zero(DivisionByZeroHandling::ReturnNull)
    .with_loose_equality_errors(false);

NaN Handling

Control how non-numeric values are handled in arithmetic operations.

use datalogic_rs::{EvaluationConfig, NanHandling};

// ThrowError (default), IgnoreValue, CoerceToZero, ReturnNull
let config = EvaluationConfig::default()
    .with_arithmetic_nan_handling(NanHandling::IgnoreValue);

Behavior comparison for {"+": [1, "text", 2]}:

SettingResult
ThrowError (default)Err(Thrown { type: "NaN" })
IgnoreValue3 (skips "text")
CoerceToZero3 ("text"0)
ReturnNullnull

Division by Zero

use datalogic_rs::{EvaluationConfig, DivisionByZeroHandling};

// ReturnSaturated (default), ThrowError, ReturnNull, ReturnInfinity
let config = EvaluationConfig::default()
    .with_division_by_zero(DivisionByZeroHandling::ThrowError);

Behavior comparison for {"/": [10, 0]}:

SettingResult
ReturnSaturated (default)f64::MAX (sign of dividend)
ThrowErrorErr(Thrown { type: "NaN" })
ReturnNullnull
ReturnInfinityInfinity (sign of dividend)

Truthiness Evaluation

use std::sync::Arc;
use datalogic_rs::{EvaluationConfig, TruthyEvaluator};
use datalogic_rs::datavalue::OwnedDataValue;

// JavaScript (default), Python, StrictBoolean, Custom
let config = EvaluationConfig::default()
    .with_truthy_evaluator(TruthyEvaluator::Python);

// Custom truthy: receives an OwnedDataValue (no serde_json required)
let custom = Arc::new(|value: &OwnedDataValue| -> bool {
    value.as_f64().map_or(false, |n| n > 0.0)
});
let config = EvaluationConfig::default()
    .with_truthy_evaluator(TruthyEvaluator::Custom(custom));

v5 change: TruthyEvaluator::Custom now takes Arc<dyn Fn(&OwnedDataValue) -> bool + Send + Sync> (the canonical owned value type). v4 used &serde_json::Value.

Truthiness comparison:

ValueJavaScriptPythonStrictBoolean
truetruthytruthytruthy
falsefalsyfalsyfalsy
1truthytruthyfalsy
0falsyfalsyfalsy
""falsyfalsyfalsy
"0"truthytruthyfalsy
[]falsyfalsyfalsy
[0]truthytruthyfalsy
nullfalsyfalsyfalsy

Loose Equality Errors

Control whether loose equality (==) raises errors for incompatible types.

let config = EvaluationConfig::default()
    .with_loose_equality_errors(true);   // default

Numeric Coercion

NumericCoercionConfig is #[non_exhaustive] too: start from default() and chain its own with_* setters, then pass it through with_numeric_coercion.

use datalogic_rs::{EvaluationConfig, NumericCoercionConfig};

let config = EvaluationConfig::default()
    .with_numeric_coercion(
        NumericCoercionConfig::default()
            .with_empty_string_to_zero(false)
            .with_null_to_zero(false)
            .with_bool_to_number(false)
            .with_reject_non_numeric(true),
    );

Max Recursion Depth

Cap the number of nested evaluation-boundary calls before the engine bails with a ConfigurationError. The limit is tracked per thread and guards against custom operators that hold an Arc<Engine> and re-enter via engine.evaluate(...). Pure built-in workloads skip the check entirely, so they pay nothing.

use datalogic_rs::EvaluationConfig;

// Default is 256: raise it for deeply nested custom-operator graphs,
// lower it to bail sooner.
let config = EvaluationConfig::default()
    .with_max_recursion_depth(256);

Configuration Presets

use datalogic_rs::{Engine, EvaluationConfig};

// Lenient arithmetic — IgnoreValue + ReturnNull divide-by-zero
let engine = Engine::builder()
    .with_config(EvaluationConfig::safe_arithmetic())
    .build();

// Strict — errors for any type mismatch and no numeric coercion
let engine = Engine::builder()
    .with_config(EvaluationConfig::strict())
    .build();

Configuring from JSON

EvaluationConfig::from_json_str (requires feature = "serde_json") builds a configuration from a JSON object. This is the wire format the language bindings use to pass engine configuration across FFI boundaries through one shared parser; Rust callers normally use the typed with_* setters above.

All keys are optional. The "preset" key is applied first, then the remaining keys override individual fields on top of it. Unknown keys and unknown enum strings are rejected with a ConfigurationError, so typos fail loudly instead of being silently ignored.

KeyValue
preset"default", "safe_arithmetic", or "strict"
arithmetic_nan_handling"throw_error", "ignore_value", "coerce_to_zero", or "return_null"
division_by_zero"return_saturated", "throw_error", "return_null", or "return_infinity"
loose_equality_errorsbool
truthy_evaluator"javascript", "python", or "strict_boolean"
numeric_coercionobject of bools: empty_string_to_zero, null_to_zero, bool_to_number, reject_non_numeric
max_recursion_depthinteger >= 1

Custom truthiness closures (TruthyEvaluator::Custom) cannot be expressed in JSON; they are available through the Rust API only.

From Rust:

use datalogic_rs::{Engine, EvaluationConfig};

let config = EvaluationConfig::from_json_str(r#"{
    "preset": "strict",
    "division_by_zero": "return_null",
    "numeric_coercion": {"null_to_zero": true},
    "max_recursion_depth": 64
}"#).unwrap();

let engine = Engine::builder().with_config(config).build();

The same JSON object is what you hand to a binding’s engine constructor. For example, to start from the lenient preset but use strict-boolean truthiness:

{
  "preset": "safe_arithmetic",
  "truthy_evaluator": "strict_boolean",
  "max_recursion_depth": 128
}

Combining with Templating Mode

Use both configuration and templating mode (requires feature = "templating"):

let config = EvaluationConfig::default()
    .with_arithmetic_nan_handling(NanHandling::CoerceToZero);

let engine = Engine::builder()
    .with_config(config)
    .with_templating(true)
    .build();

Configuration Examples

Lenient Data Processing

let config = EvaluationConfig::default()
    .with_arithmetic_nan_handling(NanHandling::IgnoreValue)
    .with_division_by_zero(DivisionByZeroHandling::ReturnNull);

let engine = Engine::builder().with_config(config).build();

let r = engine.eval_str(
    r#"{"+": [1, "not a number", null, 2]}"#,
    r#"{}"#,
).unwrap();
// "3" (ignores non-numeric values)

Strict Validation

let engine = Engine::builder()
    .with_config(EvaluationConfig::strict())
    .build();

let result = engine.eval_str(r#"{"+": [1, "2"]}"#, r#"{}"#);
// Err(...) — strict mode does not coerce "2" to a number

Custom Business Logic Truthiness

use std::sync::Arc;
use datalogic_rs::datavalue::OwnedDataValue;

let custom_truthy = Arc::new(|value: &OwnedDataValue| -> bool {
    match value {
        OwnedDataValue::Bool(b) => *b,
        OwnedDataValue::Number(_) => value.as_f64().map_or(false, |n| n > 0.0),
        OwnedDataValue::String(s) => !s.is_empty(),
        _ => false,
    }
});

let config = EvaluationConfig::default()
    .with_truthy_evaluator(TruthyEvaluator::Custom(custom_truthy));

let engine = Engine::builder().with_config(config).build();
// {"if": [0,  "yes", "no"]}  ⇒ "no"
// {"if": [-5, "yes", "no"]}  ⇒ "no"
// {"if": [1,  "yes", "no"]}  ⇒ "yes"