nodedb_query/expr/
binary.rs1use rust_decimal::Decimal;
6
7use nodedb_types::Value;
8
9use crate::value_ops::{
10 coerced_eq, compare_values, is_truthy, to_value_number, value_to_display_string, value_to_f64,
11};
12
13use super::types::BinaryOp;
14
15fn value_to_decimal(v: &Value) -> Option<Decimal> {
23 match v {
24 Value::Decimal(d) => Some(*d),
25 Value::Integer(i) => Some(Decimal::from(*i)),
26 Value::Float(f) => Decimal::try_from(*f).ok(),
27 _ => None,
28 }
29}
30
31fn decimal_arith(a: Decimal, op: BinaryOp, b: Decimal) -> Value {
33 let result = match op {
34 BinaryOp::Add => a.checked_add(b),
35 BinaryOp::Sub => a.checked_sub(b),
36 BinaryOp::Mul => a.checked_mul(b),
37 BinaryOp::Div => a.checked_div(b),
38 BinaryOp::Mod => a.checked_rem(b),
39 _ => return Value::Null,
40 };
41 result.map(Value::Decimal).unwrap_or(Value::Null)
42}
43
44pub(super) fn eval_binary_op(left: &Value, op: BinaryOp, right: &Value) -> Value {
45 match op {
46 BinaryOp::Add | BinaryOp::Sub | BinaryOp::Mul | BinaryOp::Div | BinaryOp::Mod => {
48 let left_is_decimal = matches!(left, Value::Decimal(_));
49 let right_is_decimal = matches!(right, Value::Decimal(_));
50 if left_is_decimal || right_is_decimal {
51 match (value_to_decimal(left), value_to_decimal(right)) {
52 (Some(a), Some(b)) => return decimal_arith(a, op, b),
53 _ => return Value::Null,
54 }
55 }
56 match op {
58 BinaryOp::Add => match (value_to_f64(left, true), value_to_f64(right, true)) {
59 (Some(a), Some(b)) => to_value_number(a + b),
60 _ => Value::Null,
61 },
62 BinaryOp::Sub => match (value_to_f64(left, true), value_to_f64(right, true)) {
63 (Some(a), Some(b)) => to_value_number(a - b),
64 _ => Value::Null,
65 },
66 BinaryOp::Mul => match (value_to_f64(left, true), value_to_f64(right, true)) {
67 (Some(a), Some(b)) => to_value_number(a * b),
68 _ => Value::Null,
69 },
70 BinaryOp::Div => match (value_to_f64(left, true), value_to_f64(right, true)) {
71 (Some(a), Some(b)) => {
72 if b == 0.0 {
73 Value::Null
74 } else {
75 to_value_number(a / b)
76 }
77 }
78 _ => Value::Null,
79 },
80 BinaryOp::Mod => match (value_to_f64(left, true), value_to_f64(right, true)) {
81 (Some(a), Some(b)) => {
82 if b == 0.0 {
83 Value::Null
84 } else {
85 to_value_number(a % b)
86 }
87 }
88 _ => Value::Null,
89 },
90 _ => Value::Null,
91 }
92 }
93 BinaryOp::Concat => {
94 let ls = value_to_display_string(left);
95 let rs = value_to_display_string(right);
96 Value::String(format!("{ls}{rs}"))
97 }
98 BinaryOp::Eq => Value::Bool(coerced_eq(left, right)),
99 BinaryOp::NotEq => Value::Bool(!coerced_eq(left, right)),
100 BinaryOp::Gt => Value::Bool(compare_values(left, right) == std::cmp::Ordering::Greater),
101 BinaryOp::GtEq => {
102 let c = compare_values(left, right);
103 Value::Bool(c == std::cmp::Ordering::Greater || c == std::cmp::Ordering::Equal)
104 }
105 BinaryOp::Lt => Value::Bool(compare_values(left, right) == std::cmp::Ordering::Less),
106 BinaryOp::LtEq => {
107 let c = compare_values(left, right);
108 Value::Bool(c == std::cmp::Ordering::Less || c == std::cmp::Ordering::Equal)
109 }
110 BinaryOp::And => Value::Bool(is_truthy(left) && is_truthy(right)),
111 BinaryOp::Or => Value::Bool(is_truthy(left) || is_truthy(right)),
112 }
113}