Skip to main content

netidx_value/
op.rs

1use crate::{Typ, ValArray, Value};
2use arcstr::literal;
3use compact_str::format_compact;
4use rust_decimal::Decimal;
5use std::{
6    cmp::{Ordering, PartialEq, PartialOrd},
7    hash::Hash,
8    iter, mem,
9    ops::{Add, Div, Mul, Not, Rem, Sub},
10    time::Duration,
11};
12use triomphe::Arc;
13
14impl Hash for Value {
15    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
16        use std::num::FpCategory::*;
17        match self {
18            Value::U32(v) => {
19                0u8.hash(state);
20                v.hash(state)
21            }
22            Value::V32(v) => {
23                1u8.hash(state);
24                v.hash(state)
25            }
26            Value::I32(v) => {
27                2u8.hash(state);
28                v.hash(state)
29            }
30            Value::Z32(v) => {
31                3u8.hash(state);
32                v.hash(state)
33            }
34            Value::U64(v) => {
35                4u8.hash(state);
36                v.hash(state)
37            }
38            Value::V64(v) => {
39                5u8.hash(state);
40                v.hash(state)
41            }
42            Value::I64(v) => {
43                6u8.hash(state);
44                v.hash(state)
45            }
46            Value::Z64(v) => {
47                7u8.hash(state);
48                v.hash(state)
49            }
50            Value::F32(v) => {
51                8u8.hash(state);
52                let bits = v.to_bits();
53                match v.classify() {
54                    Nan => ((bits & 0xFF00_0000) | 0x1).hash(state), // normalize NaN
55                    _ => bits.hash(state),
56                }
57            }
58            Value::F64(v) => {
59                9u8.hash(state);
60                let bits = v.to_bits();
61                match v.classify() {
62                    Nan => ((bits & 0xFFE0_0000_0000_0000) | 0x1).hash(state), // normalize NaN
63                    _ => bits.hash(state),
64                }
65            }
66            Value::DateTime(d) => {
67                10u8.hash(state);
68                d.hash(state)
69            }
70            Value::Duration(d) => {
71                11u8.hash(state);
72                d.hash(state)
73            }
74            Value::String(c) => {
75                12u8.hash(state);
76                c.hash(state)
77            }
78            Value::Bytes(b) => {
79                13u8.hash(state);
80                b.hash(state)
81            }
82            Value::Bool(true) => 14u8.hash(state),
83            Value::Bool(false) => 15u8.hash(state),
84            Value::Null => 16u8.hash(state),
85            Value::Error(c) => match &**c {
86                Value::String(e) => {
87                    18u8.hash(state);
88                    e.hash(state)
89                }
90                v => {
91                    21u8.hash(state);
92                    v.hash(state)
93                }
94            },
95            Value::Array(a) => {
96                19u8.hash(state);
97                for v in a.iter() {
98                    v.hash(state)
99                }
100            }
101            Value::Decimal(d) => {
102                20u8.hash(state);
103                d.hash(state);
104            }
105            Value::Map(m) => {
106                21u8.hash(state);
107                m.hash(state);
108            }
109            Value::U8(v) => {
110                22u8.hash(state);
111                v.hash(state)
112            }
113            Value::I8(v) => {
114                23u8.hash(state);
115                v.hash(state)
116            }
117            Value::U16(v) => {
118                24u8.hash(state);
119                v.hash(state)
120            }
121            Value::I16(v) => {
122                25u8.hash(state);
123                v.hash(state)
124            }
125            Value::Abstract(v) => {
126                26u8.hash(state);
127                v.hash(state)
128            }
129        }
130    }
131}
132
133impl PartialEq for Value {
134    fn eq(&self, rhs: &Value) -> bool {
135        use std::num::FpCategory::*;
136        Typ::get(self) == Typ::get(rhs)
137            && match (self, rhs) {
138                (Value::U8(l), Value::U8(r)) => l == r,
139                (Value::I8(l), Value::I8(r)) => l == r,
140                (Value::U16(l), Value::U16(r)) => l == r,
141                (Value::I16(l), Value::I16(r)) => l == r,
142                (Value::U32(l), Value::U32(r)) => l == r,
143                (Value::V32(l), Value::V32(r)) => l == r,
144                (Value::I32(l), Value::I32(r)) => l == r,
145                (Value::Z32(l), Value::Z32(r)) => l == r,
146                (Value::U64(l), Value::U64(r)) => l == r,
147                (Value::V64(l), Value::V64(r)) => l == r,
148                (Value::I64(l), Value::I64(r)) => l == r,
149                (Value::Z64(l), Value::Z64(r)) => l == r,
150                (Value::F32(l), Value::F32(r)) => match (l.classify(), r.classify()) {
151                    (Nan, Nan) => true,
152                    (_, _) => l == r,
153                },
154                (Value::F64(l), Value::F64(r)) => match (l.classify(), r.classify()) {
155                    (Nan, Nan) => true,
156                    (_, _) => l == r,
157                },
158                (Value::Decimal(l), Value::Decimal(r)) => l == r,
159                (Value::DateTime(l), Value::DateTime(r)) => l == r,
160                (Value::Duration(l), Value::Duration(r)) => l == r,
161                (Value::Bool(l), Value::Bool(r)) => l == r,
162                (Value::Null, Value::Null) => true,
163                (Value::String(l), Value::String(r)) => l == r,
164                (Value::Bytes(l), Value::Bytes(r)) => l == r,
165                (Value::Error(l), Value::Error(r)) => l == r,
166                (Value::Array(l), Value::Array(r)) => l == r,
167                (Value::Map(l), Value::Map(r)) => l == r,
168                (Value::Abstract(l), Value::Abstract(r)) => l == r,
169                (_, _) => false,
170            }
171    }
172}
173
174impl Eq for Value {}
175
176impl PartialOrd for Value {
177    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
178        use std::num::FpCategory::*;
179        match Typ::get(self).cmp(&Typ::get(other)) {
180            Ordering::Greater => Some(Ordering::Greater),
181            Ordering::Less => Some(Ordering::Less),
182            Ordering::Equal => match (self, other) {
183                (Value::U8(l), Value::U8(r)) => l.partial_cmp(r),
184                (Value::I8(l), Value::I8(r)) => l.partial_cmp(r),
185                (Value::U16(l), Value::U16(r)) => l.partial_cmp(r),
186                (Value::I16(l), Value::I16(r)) => l.partial_cmp(r),
187                (Value::U32(l), Value::U32(r)) => l.partial_cmp(r),
188                (Value::V32(l), Value::V32(r)) => l.partial_cmp(r),
189                (Value::I32(l), Value::I32(r)) => l.partial_cmp(r),
190                (Value::Z32(l), Value::Z32(r)) => l.partial_cmp(r),
191                (Value::U64(l), Value::U64(r)) => l.partial_cmp(r),
192                (Value::V64(l), Value::V64(r)) => l.partial_cmp(r),
193                (Value::I64(l), Value::I64(r)) => l.partial_cmp(r),
194                (Value::Z64(l), Value::Z64(r)) => l.partial_cmp(r),
195                (Value::F32(l), Value::F32(r)) => match (l.classify(), r.classify()) {
196                    (Nan, Nan) => Some(Ordering::Equal),
197                    (Nan, _) => Some(Ordering::Less),
198                    (_, Nan) => Some(Ordering::Greater),
199                    (_, _) => l.partial_cmp(r),
200                },
201                (Value::F64(l), Value::F64(r)) => match (l.classify(), r.classify()) {
202                    (Nan, Nan) => Some(Ordering::Equal),
203                    (Nan, _) => Some(Ordering::Less),
204                    (_, Nan) => Some(Ordering::Greater),
205                    (_, _) => l.partial_cmp(r),
206                },
207                (Value::Decimal(l), Value::Decimal(r)) => l.partial_cmp(r),
208                (Value::DateTime(l), Value::DateTime(r)) => l.partial_cmp(r),
209                (Value::Duration(l), Value::Duration(r)) => l.partial_cmp(r),
210                (Value::Bool(l), Value::Bool(r)) => l.partial_cmp(r),
211                (Value::Null, Value::Null) => Some(Ordering::Equal),
212                (Value::String(l), Value::String(r)) => l.partial_cmp(r),
213                (Value::Bytes(l), Value::Bytes(r)) => l.partial_cmp(r),
214                (Value::Error(l), Value::Error(r)) => l.partial_cmp(r),
215                (Value::Array(l), Value::Array(r)) => l.partial_cmp(r),
216                (Value::Map(l), Value::Map(r)) => l.partial_cmp(r),
217                (Value::Abstract(l), Value::Abstract(r)) => l.partial_cmp(r),
218                (_, _) => unreachable!(),
219            },
220        }
221    }
222}
223
224impl Ord for Value {
225    fn cmp(&self, other: &Self) -> Ordering {
226        self.partial_cmp(other).unwrap()
227    }
228}
229
230macro_rules! int_op {
231    ($lhs:expr, wrapping, $method:ident, $rhs:expr, $Ctor:ident) => {
232        Value::$Ctor(($lhs).$method($rhs))
233    };
234    ($lhs:expr, checked, $method:ident, $rhs:expr, $Ctor:ident) => {
235        match ($lhs).$method($rhs) {
236            Some(v) => Value::$Ctor(v),
237            None => return Value::error(literal!("arithmetic error")),
238        }
239    };
240}
241
242macro_rules! checked_decimal {
243    ($lhs:expr, $dec_method:ident, $rhs:expr) => {
244        match ($lhs).$dec_method($rhs) {
245            Some(v) => Value::Decimal(Arc::new(v)),
246            None => return Value::error(literal!("decimal arithmetic error")),
247        }
248    };
249}
250
251macro_rules! checked_dur {
252    ($expr:expr) => {
253        match $expr {
254            Some(v) => Value::Duration(Arc::new(v)),
255            None => return Value::error(literal!("duration arithmetic error")),
256        }
257    };
258}
259
260macro_rules! dur_from_f64 {
261    ($expr:expr) => {
262        match Duration::try_from_secs_f64($expr) {
263            Ok(v) => Value::Duration(Arc::new(v)),
264            Err(_) => return Value::error(literal!("duration arithmetic error")),
265        }
266    };
267}
268
269macro_rules! apply_op_mixed_int {
270    ($lhs:expr, $rhs:expr, $lhc:ident, $lht:ty, $op:tt, $mode:ident, $int_method:ident, $dec_method:ident) => {
271        match $rhs {
272            Value::U8(r) => int_op!($lhs, $mode, $int_method, r as $lht, $lhc),
273            Value::I8(r) => int_op!($lhs, $mode, $int_method, r as $lht, $lhc),
274            Value::U16(r) if mem::size_of::<u16>() > mem::size_of::<$lht>() => int_op!(($lhs as u16), $mode, $int_method, r, U16),
275            Value::U16(r) => int_op!($lhs, $mode, $int_method, r as $lht, $lhc),
276            Value::I16(r) if mem::size_of::<i16>() > mem::size_of::<$lht>() => int_op!(($lhs as i16), $mode, $int_method, r, I16),
277            Value::I16(r) => int_op!($lhs, $mode, $int_method, r as $lht, $lhc),
278            Value::U32(r) | Value::V32(r) if mem::size_of::<u32>() > mem::size_of::<$lht>() => int_op!(($lhs as u32), $mode, $int_method, r, U32),
279            Value::U32(r) | Value::V32(r) => int_op!($lhs, $mode, $int_method, r as $lht, $lhc),
280            Value::I32(r) | Value::Z32(r) if mem::size_of::<i32>() > mem::size_of::<$lht>() => int_op!(($lhs as i32), $mode, $int_method, r, I32),
281            Value::I32(r) | Value::Z32(r) => int_op!($lhs, $mode, $int_method, r as $lht, $lhc),
282            Value::U64(r) | Value::V64(r) if mem::size_of::<u64>() > mem::size_of::<$lht>() => int_op!(($lhs as u64), $mode, $int_method, r, U64),
283            Value::U64(r) | Value::V64(r) => int_op!($lhs, $mode, $int_method, r as $lht, $lhc),
284            Value::I64(r) | Value::Z64(r) if mem::size_of::<i64>() > mem::size_of::<$lht>() => int_op!(($lhs as i64), $mode, $int_method, r, I64),
285            Value::I64(r) | Value::Z64(r) => int_op!($lhs, $mode, $int_method, r as $lht, $lhc),
286            Value::F32(r) => Value::F32(($lhs as f32) $op r),
287            Value::F64(r) => Value::F64(($lhs as f64) $op r),
288            Value::Decimal(r) => checked_decimal!(Decimal::from($lhs), $dec_method, *r),
289            _ => unreachable!(),
290        }
291    }
292}
293
294macro_rules! apply_op_mixed_float {
295    ($lhs:expr, $rhs:expr, $lhc:ident, $lht:ty, $op:tt, $dec_method:ident) => {
296        match $rhs {
297            Value::U8(r) => Value::$lhc($lhs $op r as $lht),
298            Value::I8(r) => Value::$lhc($lhs $op r as $lht),
299            Value::U16(r) => Value::$lhc($lhs $op r as $lht),
300            Value::I16(r) => Value::$lhc($lhs $op r as $lht),
301            Value::U32(r) | Value::V32(r) => Value::$lhc($lhs $op r as $lht),
302            Value::I32(r) | Value::Z32(r) => Value::$lhc($lhs $op r as $lht),
303            Value::U64(r) | Value::V64(r) => Value::$lhc($lhs $op r as $lht),
304            Value::I64(r) | Value::Z64(r) => Value::$lhc($lhs $op r as $lht),
305            Value::F32(r) => Value::$lhc($lhs $op r as $lht),
306            Value::F64(r) => Value::$lhc($lhs $op r as $lht),
307            Value::Decimal(r) => {
308                let d = match Decimal::try_from($lhs) {
309                    Err(e) => return Value::error(format!("could not convert {e:?} to decimal")),
310                    Ok(d) => d
311                };
312                checked_decimal!(d, $dec_method, *r)
313            },
314            _ => unreachable!(),
315        }
316    }
317}
318
319macro_rules! apply_op_mixed_decimal {
320    ($lhs:expr, $rhs:expr, $dec_method:ident) => {
321        match $rhs {
322            Value::U8(r) => checked_decimal!($lhs, $dec_method, Decimal::from(r)),
323            Value::I8(r) => checked_decimal!($lhs, $dec_method, Decimal::from(r)),
324            Value::U16(r) => checked_decimal!($lhs, $dec_method, Decimal::from(r)),
325            Value::I16(r) => checked_decimal!($lhs, $dec_method, Decimal::from(r)),
326            Value::U32(r) | Value::V32(r) => checked_decimal!($lhs, $dec_method, Decimal::from(r)),
327            Value::I32(r) | Value::Z32(r) => checked_decimal!($lhs, $dec_method, Decimal::from(r)),
328            Value::U64(r) | Value::V64(r) => checked_decimal!($lhs, $dec_method, Decimal::from(r)),
329            Value::I64(r) | Value::Z64(r) => checked_decimal!($lhs, $dec_method, Decimal::from(r)),
330            Value::F32(r) => {
331                let d = match Decimal::try_from(r) {
332                    Err(e) => return Value::error(format!("could not convert {e:?} to decimal")),
333                    Ok(d) => d
334                };
335                checked_decimal!($lhs, $dec_method, d)
336            },
337            Value::F64(r) => {
338                let d = match Decimal::try_from(r) {
339                    Err(e) => return Value::error(format!("could not convert {e:?} to decimal")),
340                    Ok(d) => d
341                };
342                checked_decimal!($lhs, $dec_method, d)
343            }
344            _ => unreachable!(),
345        }
346    }
347}
348
349macro_rules! number {
350    () => {
351        Value::U8(_)
352            | Value::I8(_)
353            | Value::U16(_)
354            | Value::I16(_)
355            | Value::U32(_)
356            | Value::V32(_)
357            | Value::I32(_)
358            | Value::Z32(_)
359            | Value::U64(_)
360            | Value::V64(_)
361            | Value::I64(_)
362            | Value::Z64(_)
363            | Value::F32(_)
364            | Value::F64(_)
365            | Value::Decimal(_)
366    };
367}
368
369macro_rules! apply_op {
370    ($self:expr, $rhs:expr, $id:expr, $op:tt, $mode:ident, $int_method:ident, $dec_method:ident, $($pat:pat => $blk:block),+) => {
371        #[allow(unreachable_patterns)]
372        match ($self, $rhs) {
373            (Value::U8(l), Value::U8(r)) => int_op!(l, $mode, $int_method, r, U8),
374            (Value::I8(l), Value::I8(r)) => int_op!(l, $mode, $int_method, r, I8),
375            (Value::U16(l), Value::U16(r)) => int_op!(l, $mode, $int_method, r, U16),
376            (Value::I16(l), Value::I16(r)) => int_op!(l, $mode, $int_method, r, I16),
377            (Value::U32(l) | Value::V32(l), Value::U32(r) | Value::V32(r)) => {
378                int_op!(l, $mode, $int_method, r, U32)
379            }
380            (Value::I32(l) | Value::Z32(l), Value::I32(r) | Value::Z32(r)) => {
381                int_op!(l, $mode, $int_method, r, I32)
382            }
383            (Value::U64(l) | Value::V64(l), Value::U64(r) | Value::V64(r)) => {
384                int_op!(l, $mode, $int_method, r, U64)
385            }
386            (Value::I64(l) | Value::Z64(l), Value::I64(r) | Value::Z64(r)) => {
387                int_op!(l, $mode, $int_method, r, I64)
388            }
389            (Value::F32(l), Value::F32(r)) => Value::F32(l $op r),
390            (Value::F64(l), Value::F64(r)) => Value::F64(l $op r),
391            (Value::Decimal(l), Value::Decimal(r)) => checked_decimal!(*l, $dec_method, *r),
392            (Value::U8(l), v@ number!()) => apply_op_mixed_int!(l, v, U8, u8, $op, $mode, $int_method, $dec_method),
393            (Value::I8(l), v@ number!()) => apply_op_mixed_int!(l, v, I8, i8, $op, $mode, $int_method, $dec_method),
394            (Value::U16(l), v@ number!()) => apply_op_mixed_int!(l, v, U16, u16, $op, $mode, $int_method, $dec_method),
395            (Value::I16(l), v@ number!()) => apply_op_mixed_int!(l, v, I16, i16, $op, $mode, $int_method, $dec_method),
396            (Value::U32(l) | Value::V32(l), v@ number!()) => apply_op_mixed_int!(l, v, U32, u32, $op, $mode, $int_method, $dec_method),
397            (Value::I32(l) | Value::Z32(l), v@ number!()) => apply_op_mixed_int!(l, v, I32, i32, $op, $mode, $int_method, $dec_method),
398            (Value::U64(l) | Value::V64(l), v@ number!()) => apply_op_mixed_int!(l, v, U64, u64, $op, $mode, $int_method, $dec_method),
399            (Value::I64(l) | Value::Z64(l), v@ number!()) => apply_op_mixed_int!(l, v, I64, i64, $op, $mode, $int_method, $dec_method),
400            (Value::F32(l), v@ number!()) => apply_op_mixed_float!(l, v, F32, f32, $op, $dec_method),
401            (Value::F64(l), v@ number!()) => apply_op_mixed_float!(l, v, F64, f64, $op, $dec_method),
402            (Value::Decimal(l), v@ number!()) => apply_op_mixed_decimal!(*l, v, $dec_method),
403            (Value::String(s), n) => match s.parse::<Value>() {
404                Err(e) => Value::error(format_compact!("{}", e).as_str()),
405                Ok(s) => s $op n,
406            }
407            (n, Value::String(s)) => match s.parse::<Value>() {
408                Err(e) => Value::error(format_compact!("{}", e).as_str()),
409                Ok(s) => n $op s,
410            },
411            (Value::Array(e0), Value::Array(e1)) => {
412                let (e0, e1) = if e0.len() < e1.len() { (e0, e1) } else { (e1, e0) };
413                let iter = e0
414                    .iter()
415                    .cloned()
416                    .chain(iter::repeat(Value::F64($id)))
417                    .zip(e1.iter().cloned());
418                Value::Array(iter.map(|(v0, v1)| v0 $op v1).collect())
419            }
420            (l @ Value::Array(_), n) => {
421                match n.cast(Typ::Array) {
422                    None => Value::error(literal!("can't add to array")),
423                    Some(r) => l $op r,
424                }
425            }
426            (n, r @ Value::Array(_)) => {
427                match n.cast(Typ::Array) {
428                    None => Value::error(literal!("can't add to array")),
429                    Some(l) => l $op r,
430                }
431            }
432            (Value::Map(_), _) | (_, Value::Map(_)) => Value::error(literal!("can't apply to Map")),
433            (Value::Bytes(_), _) | (_, Value::Bytes(_)) => {
434                Value::error(literal!("can't add bytes"))
435            }
436            (Value::Null, _) | (_, Value::Null) => {
437                Value::error(literal!("can't add null"))
438            }
439            | (Value::Error(_), _)
440                | (_, Value::Error(_)) => Value::error(literal!("can't add error types")),
441            (Value::Abstract(_), _) | (_, Value::Abstract(_)) => Value::error(literal!("can't add abstract types")),
442            (Value::Bool(true), n) => Value::U32(1) $op n,
443            (n, Value::Bool(true)) => n $op Value::U32(1),
444            (Value::Bool(false), n) => Value::U32(0) $op n,
445            (n, Value::Bool(false)) => n $op Value::U32(0),
446            $($pat => $blk),+
447        }
448    }
449}
450
451impl Add for Value {
452    type Output = Value;
453
454    fn add(self, rhs: Self) -> Self {
455        apply_op!(
456            self, rhs, 0., +, wrapping, wrapping_add, checked_add,
457            (Value::DateTime(dt), Value::Duration(d))
458                | (Value::Duration(d), Value::DateTime(dt)) => {
459                    match chrono::Duration::from_std(*d) {
460                        Ok(d) => Value::DateTime(Arc::new((*dt) + d)),
461                        Err(e) => Value::error(format_compact!("{}", e).as_str()),
462                    }
463                },
464            (Value::Duration(d0), Value::Duration(d1)) => {
465                checked_dur!(d0.checked_add(*d1))
466            },
467            (Value::Duration(_), _)
468                | (_, Value::Duration(_))
469                | (_, Value::DateTime(_))
470                | (Value::DateTime(_), _) => {
471                    Value::error(literal!("can't add to datetime/duration"))
472                }
473        )
474    }
475}
476
477impl Sub for Value {
478    type Output = Value;
479
480    fn sub(self, rhs: Self) -> Self {
481        apply_op!(
482            self, rhs, 0., -, wrapping, wrapping_sub, checked_sub,
483            (Value::DateTime(dt), Value::Duration(d))
484                | (Value::Duration(d), Value::DateTime(dt)) => {
485                    match chrono::Duration::from_std(*d) {
486                        Ok(d) => Value::DateTime(Arc::new((*dt) - d)),
487                        Err(e) => Value::error(format_compact!("{}", e).as_str()),
488                    }
489                },
490            (Value::Duration(d0), Value::Duration(d1)) => {
491                checked_dur!(d0.checked_sub(*d1))
492            },
493            (Value::Duration(_), _)
494                | (_, Value::Duration(_))
495                | (_, Value::DateTime(_))
496                | (Value::DateTime(_), _) => {
497                    Value::error(literal!("can't sub datetime/duration"))
498                }
499        )
500    }
501}
502
503impl Mul for Value {
504    type Output = Value;
505
506    fn mul(self, rhs: Self) -> Self {
507        apply_op!(
508            self, rhs, 1., *, wrapping, wrapping_mul, checked_mul,
509            (Value::Duration(d), Value::U32(n) | Value::V32(n))
510            | (Value::U32(n) | Value::V32(n), Value::Duration(d)) => {
511                checked_dur!(d.checked_mul(n))
512            },
513            (Value::Duration(d), Value::I32(n) | Value::Z32(n))
514            | (Value::I32(n) | Value::Z32(n), Value::Duration(d)) => {
515                if n < 0 { return Value::error(literal!("can't multiply duration by negative")); }
516                checked_dur!(d.checked_mul(n as u32))
517            },
518            (Value::Duration(d), Value::U64(n) | Value::V64(n))
519            | (Value::U64(n) | Value::V64(n), Value::Duration(d)) => {
520                checked_dur!(d.checked_mul(n as u32))
521            },
522            (Value::Duration(d), Value::I64(n) | Value::Z64(n))
523            | (Value::I64(n) | Value::Z64(n), Value::Duration(d)) => {
524                if n < 0 { return Value::error(literal!("can't multiply duration by negative")); }
525                checked_dur!(d.checked_mul(n as u32))
526            },
527            (Value::Duration(d), Value::F32(s)) | (Value::F32(s), Value::Duration(d)) => {
528                dur_from_f64!(d.as_secs_f64() * s as f64)
529            },
530            (Value::Duration(d), Value::F64(s)) | (Value::F64(s), Value::Duration(d)) => {
531                dur_from_f64!(d.as_secs_f64() * s)
532            },
533                | (Value::Duration(_), _)
534                | (_, Value::Duration(_))
535                | (_, Value::DateTime(_))
536                | (Value::DateTime(_), _) => {
537                    Value::error(literal!("can't mul datetime/duration"))
538                }
539        )
540    }
541}
542
543impl Div for Value {
544    type Output = Value;
545
546    fn div(self, rhs: Self) -> Self {
547        apply_op!(
548            self, rhs, 1., /, checked, checked_div, checked_div,
549            (Value::Duration(d), Value::U32(s) | Value::V32(s)) => {
550                checked_dur!(d.checked_div(s))
551            },
552            (Value::Duration(d), Value::I32(s) | Value::Z32(s)) => {
553                if s < 0 { return Value::error(literal!("can't divide duration by negative")); }
554                checked_dur!(d.checked_div(s as u32))
555            },
556            (Value::Duration(d), Value::U64(s) | Value::V64(s)) => {
557                checked_dur!(d.checked_div(s as u32))
558            },
559            (Value::Duration(d), Value::I64(s) | Value::Z64(s)) => {
560                if s < 0 { return Value::error(literal!("can't divide duration by negative")); }
561                checked_dur!(d.checked_div(s as u32))
562            },
563            (Value::Duration(d), Value::F32(s)) => {
564                dur_from_f64!(d.as_secs_f64() / s as f64)
565            },
566            (Value::Duration(d), Value::F64(s)) => {
567                dur_from_f64!(d.as_secs_f64() / s)
568            },
569            (Value::Duration(_), _)
570                | (_, Value::Duration(_))
571                | (_, Value::DateTime(_))
572                | (Value::DateTime(_), _) => {
573                    Value::error(literal!("can't div datetime/duration"))
574                }
575        )
576    }
577}
578
579impl Rem for Value {
580    type Output = Value;
581
582    fn rem(self, rhs: Self) -> Self::Output {
583        apply_op!(
584            self, rhs, 1., %, checked, checked_rem, checked_rem,
585            (Value::Duration(_), _)
586                | (_, Value::Duration(_))
587                | (_, Value::DateTime(_))
588                | (Value::DateTime(_), _) => {
589                    Value::error(literal!("can't mod datetime/duration"))
590                }
591        )
592    }
593}
594
595impl Value {
596    pub fn checked_add(self, rhs: Self) -> Self {
597        apply_op!(
598            self, rhs, 0., +, checked, checked_add, checked_add,
599            (Value::DateTime(dt), Value::Duration(d))
600                | (Value::Duration(d), Value::DateTime(dt)) => {
601                    match chrono::Duration::from_std(*d) {
602                        Ok(d) => Value::DateTime(Arc::new((*dt) + d)),
603                        Err(e) => Value::error(format_compact!("{}", e).as_str()),
604                    }
605                },
606            (Value::Duration(d0), Value::Duration(d1)) => {
607                checked_dur!(d0.checked_add(*d1))
608            },
609            (Value::Duration(_), _)
610                | (_, Value::Duration(_))
611                | (_, Value::DateTime(_))
612                | (Value::DateTime(_), _) => {
613                    Value::error(literal!("can't add to datetime/duration"))
614                }
615        )
616    }
617
618    pub fn checked_sub(self, rhs: Self) -> Self {
619        apply_op!(
620            self, rhs, 0., -, checked, checked_sub, checked_sub,
621            (Value::DateTime(dt), Value::Duration(d))
622                | (Value::Duration(d), Value::DateTime(dt)) => {
623                    match chrono::Duration::from_std(*d) {
624                        Ok(d) => Value::DateTime(Arc::new((*dt) - d)),
625                        Err(e) => Value::error(format_compact!("{}", e).as_str()),
626                    }
627                },
628            (Value::Duration(d0), Value::Duration(d1)) => {
629                checked_dur!(d0.checked_sub(*d1))
630            },
631            (Value::Duration(_), _)
632                | (_, Value::Duration(_))
633                | (_, Value::DateTime(_))
634                | (Value::DateTime(_), _) => {
635                    Value::error(literal!("can't sub datetime/duration"))
636                }
637        )
638    }
639
640    pub fn checked_mul(self, rhs: Self) -> Self {
641        apply_op!(
642            self, rhs, 1., *, checked, checked_mul, checked_mul,
643            (Value::Duration(d), Value::U32(n) | Value::V32(n))
644            | (Value::U32(n) | Value::V32(n), Value::Duration(d)) => {
645                checked_dur!(d.checked_mul(n))
646            },
647            (Value::Duration(d), Value::I32(n) | Value::Z32(n))
648            | (Value::I32(n) | Value::Z32(n), Value::Duration(d)) => {
649                if n < 0 { return Value::error(literal!("can't multiply duration by negative")); }
650                checked_dur!(d.checked_mul(n as u32))
651            },
652            (Value::Duration(d), Value::U64(n) | Value::V64(n))
653            | (Value::U64(n) | Value::V64(n), Value::Duration(d)) => {
654                checked_dur!(d.checked_mul(n as u32))
655            },
656            (Value::Duration(d), Value::I64(n) | Value::Z64(n))
657            | (Value::I64(n) | Value::Z64(n), Value::Duration(d)) => {
658                if n < 0 { return Value::error(literal!("can't multiply duration by negative")); }
659                checked_dur!(d.checked_mul(n as u32))
660            },
661            (Value::Duration(d), Value::F32(s)) | (Value::F32(s), Value::Duration(d)) => {
662                dur_from_f64!(d.as_secs_f64() * s as f64)
663            },
664            (Value::Duration(d), Value::F64(s)) | (Value::F64(s), Value::Duration(d)) => {
665                dur_from_f64!(d.as_secs_f64() * s)
666            },
667                | (Value::Duration(_), _)
668                | (_, Value::Duration(_))
669                | (_, Value::DateTime(_))
670                | (Value::DateTime(_), _) => {
671                    Value::error(literal!("can't mul datetime/duration"))
672                }
673        )
674    }
675
676    pub fn checked_div(self, rhs: Self) -> Self {
677        apply_op!(
678            self, rhs, 1., /, checked, checked_div, checked_div,
679            (Value::Duration(d), Value::U32(s) | Value::V32(s)) => {
680                checked_dur!(d.checked_div(s))
681            },
682            (Value::Duration(d), Value::I32(s) | Value::Z32(s)) => {
683                if s < 0 { return Value::error(literal!("can't divide duration by negative")); }
684                checked_dur!(d.checked_div(s as u32))
685            },
686            (Value::Duration(d), Value::U64(s) | Value::V64(s)) => {
687                checked_dur!(d.checked_div(s as u32))
688            },
689            (Value::Duration(d), Value::I64(s) | Value::Z64(s)) => {
690                if s < 0 { return Value::error(literal!("can't divide duration by negative")); }
691                checked_dur!(d.checked_div(s as u32))
692            },
693            (Value::Duration(d), Value::F32(s)) => {
694                dur_from_f64!(d.as_secs_f64() / s as f64)
695            },
696            (Value::Duration(d), Value::F64(s)) => {
697                dur_from_f64!(d.as_secs_f64() / s)
698            },
699            (Value::Duration(_), _)
700                | (_, Value::Duration(_))
701                | (_, Value::DateTime(_))
702                | (Value::DateTime(_), _) => {
703                    Value::error(literal!("can't div datetime/duration"))
704                }
705        )
706    }
707
708    pub fn checked_rem(self, rhs: Self) -> Self {
709        apply_op!(
710            self, rhs, 1., %, checked, checked_rem, checked_rem,
711            (Value::Duration(_), _)
712                | (_, Value::Duration(_))
713                | (_, Value::DateTime(_))
714                | (Value::DateTime(_), _) => {
715                    Value::error(literal!("can't mod datetime/duration"))
716                }
717        )
718    }
719}
720
721impl Not for Value {
722    type Output = Value;
723
724    fn not(self) -> Self {
725        match self {
726            Value::Bool(v) => Value::Bool(!v),
727            Value::Null => Value::Null,
728            Value::U8(v) => Value::U8(!v),
729            Value::I8(v) => Value::I8(!v),
730            Value::U16(v) => Value::U16(!v),
731            Value::I16(v) => Value::I16(!v),
732            Value::U32(v) => Value::U32(!v),
733            Value::V32(v) => Value::V32(!v),
734            Value::I32(v) => Value::I32(!v),
735            Value::Z32(v) => Value::Z32(!v),
736            Value::U64(v) => Value::U64(!v),
737            Value::V64(v) => Value::V64(!v),
738            Value::I64(v) => Value::I64(!v),
739            Value::Z64(v) => Value::Z64(!v),
740            Value::F32(_) => Value::error(literal!("can't apply not to F32")),
741            Value::F64(_) => Value::error(literal!("can't apply not to F64")),
742            Value::Decimal(_) => Value::error(literal!("can't apply not to Decimal")),
743            Value::DateTime(_) => Value::error(literal!("can't apply not to DateTime")),
744            Value::Duration(_) => Value::error(literal!("can't apply not to Duration")),
745            Value::String(_) => Value::error(literal!("can't apply not to String")),
746            Value::Bytes(_) => Value::error(literal!("can't apply not to Bytes")),
747            Value::Error(_) => Value::error(literal!("can't apply not to Error")),
748            Value::Map(_) => Value::error(literal!("can't apply not to Map")),
749            Value::Abstract(_) => Value::error(literal!("can't apply not to Abstract")),
750            Value::Array(elts) => {
751                Value::Array(ValArray::from_iter_exact(elts.iter().cloned().map(|v| !v)))
752            }
753        }
754    }
755}