1#![deny(clippy::arithmetic_side_effects)]
2#![allow(clippy::cast_precision_loss, clippy::module_name_repetitions)]
3
4use std::ops::{Add, Mul, Rem};
5
6use crate::compiler::{
7 ExpressionError,
8 value::{Kind, VrlValueConvert},
9};
10use crate::value::{ObjectMap, Value};
11use bytes::{BufMut, Bytes, BytesMut};
12
13use super::ValueError;
14
15#[allow(clippy::missing_errors_doc)]
16pub trait VrlValueArithmetic: Sized {
17 fn try_mul(self, rhs: Self) -> Result<Self, ValueError>;
19
20 fn try_div(self, rhs: Self) -> Result<Self, ValueError>;
22
23 fn try_add(self, rhs: Self) -> Result<Self, ValueError>;
25
26 fn try_sub(self, rhs: Self) -> Result<Self, ValueError>;
28
29 fn try_or(self, rhs: impl FnMut() -> Result<Self, ExpressionError>)
35 -> Result<Self, ValueError>;
36
37 fn try_and(self, rhs: Self) -> Result<Self, ValueError>;
41
42 fn try_rem(self, rhs: Self) -> Result<Self, ValueError>;
44
45 fn try_gt(self, rhs: Self) -> Result<Self, ValueError>;
47
48 fn try_ge(self, rhs: Self) -> Result<Self, ValueError>;
50
51 fn try_lt(self, rhs: Self) -> Result<Self, ValueError>;
53
54 fn try_le(self, rhs: Self) -> Result<Self, ValueError>;
56
57 fn try_merge(self, rhs: Self) -> Result<Self, ValueError>;
58
59 fn eq_lossy(&self, rhs: &Self) -> bool;
62}
63
64fn safe_sub(lhv: f64, rhv: f64) -> Option<Value> {
65 let result = lhv - rhv;
66 if result.is_nan() {
67 None
68 } else {
69 Some(Value::from_f64_or_zero(result))
70 }
71}
72
73impl VrlValueArithmetic for Value {
74 fn try_mul(self, rhs: Self) -> Result<Self, ValueError> {
76 let err = || ValueError::Mul(self.kind(), rhs.kind());
77
78 #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
81 let as_usize = |num| if num < 0 { 0 } else { num as usize };
82
83 let value = match self {
84 Value::Integer(lhv) if rhs.is_bytes() => {
85 Bytes::from(rhs.try_bytes()?.repeat(as_usize(lhv))).into()
86 }
87 Value::Integer(lhv) if rhs.is_float() => {
88 Value::from_f64_or_zero(lhv as f64 * rhs.try_float()?)
89 }
90 Value::Integer(lhv) => {
91 let rhv_i64 = rhs.try_into_i64().map_err(|_| err())?;
92 i64::wrapping_mul(lhv, rhv_i64).into()
93 }
94 Value::Float(lhv) => {
95 let rhs = rhs.try_into_f64().map_err(|_| err())?;
96 lhv.mul(rhs).into()
97 }
98 Value::Bytes(lhv) if rhs.is_integer() => {
99 Bytes::from(lhv.repeat(as_usize(rhs.try_integer()?))).into()
100 }
101 _ => return Err(err()),
102 };
103
104 Ok(value)
105 }
106
107 fn try_div(self, rhs: Self) -> Result<Self, ValueError> {
109 let err = || ValueError::Div(self.kind(), rhs.kind());
110
111 let rhv_f64 = rhs.try_into_f64().map_err(|_| err())?;
112
113 if rhv_f64 == 0.0 {
114 return Err(ValueError::DivideByZero);
115 }
116
117 let value = match self {
118 Value::Integer(lhv) => Value::from_f64_or_zero(lhv as f64 / rhv_f64),
119 Value::Float(lhv) => Value::from_f64_or_zero(lhv.into_inner() / rhv_f64),
120 _ => return Err(err()),
121 };
122
123 Ok(value)
124 }
125
126 fn try_add(self, rhs: Self) -> Result<Self, ValueError> {
128 let value = match (self, rhs) {
129 (Value::Integer(lhs), Value::Float(rhs)) => Value::from_f64_or_zero(lhs as f64 + *rhs),
130 (Value::Integer(lhs), rhs) => {
131 let rhv_i64 = rhs
132 .try_into_i64()
133 .map_err(|_| ValueError::Add(Kind::integer(), rhs.kind()))?;
134 i64::wrapping_add(lhs, rhv_i64).into()
135 }
136 (Value::Float(lhs), rhs) => {
137 let rhs = rhs
138 .try_into_f64()
139 .map_err(|_| ValueError::Add(Kind::float(), rhs.kind()))?;
140 lhs.add(rhs).into()
141 }
142 (lhs @ Value::Bytes(_), Value::Null) => lhs,
143 (Value::Bytes(lhs), Value::Bytes(rhs)) => {
144 #[allow(clippy::arithmetic_side_effects)]
145 let mut value = BytesMut::with_capacity(lhs.len() + rhs.len());
146 value.put(lhs);
147 value.put(rhs);
148 value.freeze().into()
149 }
150 (Value::Null, rhs @ Value::Bytes(_)) => rhs,
151 (lhs, rhs) => return Err(ValueError::Add(lhs.kind(), rhs.kind())),
152 };
153
154 Ok(value)
155 }
156
157 fn try_sub(self, rhs: Self) -> Result<Self, ValueError> {
159 let err = || ValueError::Sub(self.kind(), rhs.kind());
160
161 let value = match self {
162 Value::Integer(lhv) if rhs.is_float() => {
163 Value::from_f64_or_zero(lhv as f64 - rhs.try_float()?)
164 }
165 Value::Integer(lhv) => {
166 let rhv_i64 = rhs.try_into_i64().map_err(|_| err())?;
167 i64::wrapping_sub(lhv, rhv_i64).into()
168 }
169 Value::Float(lhs) => {
170 let rhs = rhs.try_into_f64().map_err(|_| err())?;
171 safe_sub(*lhs, rhs).ok_or_else(err)?
172 }
173 _ => return Err(err()),
174 };
175
176 Ok(value)
177 }
178
179 fn try_or(
185 self,
186 mut rhs: impl FnMut() -> Result<Self, ExpressionError>,
187 ) -> Result<Self, ValueError> {
188 let err = ValueError::Or;
189
190 match self {
191 Value::Null | Value::Boolean(false) => rhs().map_err(err),
192 value => Ok(value),
193 }
194 }
195
196 fn try_and(self, rhs: Self) -> Result<Self, ValueError> {
200 let err = || ValueError::And(self.kind(), rhs.kind());
201
202 let value = match self {
203 Value::Null => false.into(),
204 Value::Boolean(left) => match rhs {
205 Value::Null => false.into(),
206 Value::Boolean(right) => (left && right).into(),
207 _ => return Err(err()),
208 },
209 _ => return Err(err()),
210 };
211
212 Ok(value)
213 }
214
215 fn try_rem(self, rhs: Self) -> Result<Self, ValueError> {
217 let err = || ValueError::Rem(self.kind(), rhs.kind());
218
219 let rhv_f64 = rhs.try_into_f64().map_err(|_| err())?;
220
221 if rhv_f64 == 0.0 {
222 return Err(ValueError::DivideByZero);
223 }
224
225 let value = match self {
226 Value::Integer(lhv) if rhs.is_float() => {
227 Value::from_f64_or_zero(lhv as f64 % rhs.try_float()?)
228 }
229 Value::Integer(left) => {
230 let right = rhs.try_into_i64().map_err(|_| err())?;
231 i64::wrapping_rem(left, right).into()
232 }
233 Value::Float(left) => {
234 let right = rhs.try_into_f64().map_err(|_| err())?;
235 left.rem(right).into()
236 }
237 _ => return Err(err()),
238 };
239
240 Ok(value)
241 }
242
243 fn try_gt(self, rhs: Self) -> Result<Self, ValueError> {
245 let err = || ValueError::Rem(self.kind(), rhs.kind());
246
247 let value = match self {
248 Value::Integer(lhv) if rhs.is_float() => (lhv as f64 > rhs.try_float()?).into(),
249 Value::Integer(lhv) => (lhv > rhs.try_into_i64().map_err(|_| err())?).into(),
250 Value::Float(lhv) => (lhv.into_inner() > rhs.try_into_f64().map_err(|_| err())?).into(),
251 Value::Bytes(lhv) => (lhv > rhs.try_bytes()?).into(),
252 Value::Timestamp(lhv) => (lhv > rhs.try_timestamp()?).into(),
253 _ => return Err(err()),
254 };
255
256 Ok(value)
257 }
258
259 fn try_ge(self, rhs: Self) -> Result<Self, ValueError> {
261 let err = || ValueError::Ge(self.kind(), rhs.kind());
262
263 let value = match self {
264 Value::Integer(lhv) if rhs.is_float() => (lhv as f64 >= rhs.try_float()?).into(),
265 Value::Integer(lhv) => (lhv >= rhs.try_into_i64().map_err(|_| err())?).into(),
266 Value::Float(lhv) => {
267 (lhv.into_inner() >= rhs.try_into_f64().map_err(|_| err())?).into()
268 }
269 Value::Bytes(lhv) => (lhv >= rhs.try_bytes()?).into(),
270 Value::Timestamp(lhv) => (lhv >= rhs.try_timestamp()?).into(),
271 _ => return Err(err()),
272 };
273
274 Ok(value)
275 }
276
277 fn try_lt(self, rhs: Self) -> Result<Self, ValueError> {
279 let err = || ValueError::Ge(self.kind(), rhs.kind());
280
281 let value = match self {
282 Value::Integer(lhv) if rhs.is_float() => ((lhv as f64) < rhs.try_float()?).into(),
283 Value::Integer(lhv) => (lhv < rhs.try_into_i64().map_err(|_| err())?).into(),
284 Value::Float(lhv) => (lhv.into_inner() < rhs.try_into_f64().map_err(|_| err())?).into(),
285 Value::Bytes(lhv) => (lhv < rhs.try_bytes()?).into(),
286 Value::Timestamp(lhv) => (lhv < rhs.try_timestamp()?).into(),
287 _ => return Err(err()),
288 };
289
290 Ok(value)
291 }
292
293 fn try_le(self, rhs: Self) -> Result<Self, ValueError> {
295 let err = || ValueError::Ge(self.kind(), rhs.kind());
296
297 let value = match self {
298 Value::Integer(lhv) if rhs.is_float() => (lhv as f64 <= rhs.try_float()?).into(),
299 Value::Integer(lhv) => (lhv <= rhs.try_into_i64().map_err(|_| err())?).into(),
300 Value::Float(lhv) => {
301 (lhv.into_inner() <= rhs.try_into_f64().map_err(|_| err())?).into()
302 }
303 Value::Bytes(lhv) => (lhv <= rhs.try_bytes()?).into(),
304 Value::Timestamp(lhv) => (lhv <= rhs.try_timestamp()?).into(),
305 _ => return Err(err()),
306 };
307
308 Ok(value)
309 }
310
311 fn try_merge(self, rhs: Self) -> Result<Self, ValueError> {
312 let err = || ValueError::Merge(self.kind(), rhs.kind());
313
314 let value = match (&self, &rhs) {
315 (Value::Object(lhv), Value::Object(right)) => lhv
316 .iter()
317 .chain(right.iter())
318 .map(|(k, v)| (k.clone(), v.clone()))
319 .collect::<ObjectMap>()
320 .into(),
321 _ => return Err(err()),
322 };
323
324 Ok(value)
325 }
326
327 fn eq_lossy(&self, rhs: &Self) -> bool {
330 use Value::{Float, Integer};
331
332 match self {
333 Integer(lhv) => rhs
334 .try_into_f64()
335 .map(|rhv| *lhv as f64 == rhv)
336 .unwrap_or(false),
337
338 Float(lhv) => rhs
339 .try_into_f64()
340 .map(|rhv| lhv.into_inner() == rhv)
341 .unwrap_or(false),
342
343 _ => self == rhs,
344 }
345 }
346}