datalogic_rs/
config.rs

1//! Configuration module for customizable DataLogic behavior
2//!
3//! This module provides configuration options to customize the evaluation behavior
4//! of the DataLogic engine while maintaining backward compatibility.
5
6use serde_json::Value;
7use std::sync::Arc;
8
9/// Main configuration structure for DataLogic evaluation behavior
10#[derive(Clone)]
11pub struct EvaluationConfig {
12    /// How to handle NaN (Not a Number) in arithmetic operations
13    pub arithmetic_nan_handling: NanHandling,
14
15    /// How to handle division by zero
16    pub division_by_zero: DivisionByZeroHandling,
17
18    /// Whether to throw errors for incompatible types in loose equality
19    pub loose_equality_errors: bool,
20
21    /// How to evaluate truthiness of values
22    pub truthy_evaluator: TruthyEvaluator,
23
24    /// Configuration for numeric coercion behavior
25    pub numeric_coercion: NumericCoercionConfig,
26}
27
28/// Defines how to handle NaN (Not a Number) scenarios in arithmetic operations
29#[derive(Clone, Debug, PartialEq)]
30pub enum NanHandling {
31    /// Throw an error when encountering non-numeric values (default)
32    ThrowError,
33    /// Ignore non-numeric values and continue with remaining values
34    IgnoreValue,
35    /// Treat non-numeric values as zero
36    CoerceToZero,
37    /// Return null when encountering non-numeric values
38    ReturnNull,
39}
40
41/// Defines how to handle division by zero
42#[derive(Clone, Debug, PartialEq)]
43pub enum DivisionByZeroHandling {
44    /// Return f64::MAX or f64::MIN based on sign (default)
45    ReturnBounds,
46    /// Throw an error
47    ThrowError,
48    /// Return null
49    ReturnNull,
50    /// Return infinity (positive or negative based on dividend sign)
51    ReturnInfinity,
52}
53
54/// Defines how to evaluate truthiness of values
55#[derive(Clone)]
56pub enum TruthyEvaluator {
57    /// JavaScript-style truthiness (default)
58    /// - false: null, false, 0, NaN, "", empty array, empty object
59    /// - true: everything else
60    JavaScript,
61
62    /// Python-style truthiness
63    /// - false: None/null, False, 0, 0.0, "", empty collections
64    /// - true: everything else
65    Python,
66
67    /// Strict boolean truthiness
68    /// - false: null, false
69    /// - true: everything else
70    StrictBoolean,
71
72    /// Custom truthiness evaluator
73    Custom(Arc<dyn Fn(&Value) -> bool + Send + Sync>),
74}
75
76/// Configuration for numeric coercion behavior
77#[derive(Clone, Debug)]
78pub struct NumericCoercionConfig {
79    /// Convert empty strings to 0 (default: true)
80    pub empty_string_to_zero: bool,
81
82    /// Convert null to 0 (default: true)
83    pub null_to_zero: bool,
84
85    /// Convert booleans to numbers (true=1, false=0) (default: true)
86    pub bool_to_number: bool,
87
88    /// Only allow strict numeric parsing (no coercion) (default: false)
89    pub strict_numeric: bool,
90
91    /// Convert undefined/missing values to 0 (default: false)
92    pub undefined_to_zero: bool,
93}
94
95impl Default for EvaluationConfig {
96    fn default() -> Self {
97        Self {
98            arithmetic_nan_handling: NanHandling::ThrowError,
99            division_by_zero: DivisionByZeroHandling::ReturnBounds,
100            loose_equality_errors: true,
101            truthy_evaluator: TruthyEvaluator::JavaScript,
102            numeric_coercion: NumericCoercionConfig::default(),
103        }
104    }
105}
106
107impl Default for NumericCoercionConfig {
108    fn default() -> Self {
109        Self {
110            empty_string_to_zero: true,
111            null_to_zero: true,
112            bool_to_number: true,
113            strict_numeric: false,
114            undefined_to_zero: false,
115        }
116    }
117}
118
119impl EvaluationConfig {
120    /// Create a new configuration with default settings
121    pub fn new() -> Self {
122        Self::default()
123    }
124
125    /// Create a configuration with safe arithmetic (ignores non-numeric values)
126    pub fn safe_arithmetic() -> Self {
127        Self {
128            arithmetic_nan_handling: NanHandling::IgnoreValue,
129            division_by_zero: DivisionByZeroHandling::ReturnNull,
130            loose_equality_errors: false,
131            ..Default::default()
132        }
133    }
134
135    /// Create a configuration with strict behavior (more errors)
136    pub fn strict() -> Self {
137        Self {
138            arithmetic_nan_handling: NanHandling::ThrowError,
139            division_by_zero: DivisionByZeroHandling::ThrowError,
140            loose_equality_errors: true,
141            numeric_coercion: NumericCoercionConfig {
142                empty_string_to_zero: false,
143                null_to_zero: false,
144                bool_to_number: false,
145                strict_numeric: true,
146                undefined_to_zero: false,
147            },
148            ..Default::default()
149        }
150    }
151
152    /// Builder method to set NaN handling
153    pub fn with_nan_handling(mut self, handling: NanHandling) -> Self {
154        self.arithmetic_nan_handling = handling;
155        self
156    }
157
158    /// Builder method to set division by zero handling
159    pub fn with_division_by_zero(mut self, handling: DivisionByZeroHandling) -> Self {
160        self.division_by_zero = handling;
161        self
162    }
163
164    /// Builder method to set loose equality error behavior
165    pub fn with_loose_equality_errors(mut self, throw_errors: bool) -> Self {
166        self.loose_equality_errors = throw_errors;
167        self
168    }
169
170    /// Builder method to set truthy evaluator
171    pub fn with_truthy_evaluator(mut self, evaluator: TruthyEvaluator) -> Self {
172        self.truthy_evaluator = evaluator;
173        self
174    }
175
176    /// Builder method to set numeric coercion config
177    pub fn with_numeric_coercion(mut self, config: NumericCoercionConfig) -> Self {
178        self.numeric_coercion = config;
179        self
180    }
181}