nu_protocol/value/
mod.rs

1mod custom_value;
2mod duration;
3mod filesize;
4mod from_value;
5mod glob;
6mod into_value;
7mod range;
8#[cfg(test)]
9mod test_derive;
10
11pub mod format;
12pub mod record;
13pub use custom_value::CustomValue;
14pub use duration::*;
15pub use filesize::*;
16pub use from_value::FromValue;
17pub use glob::*;
18pub use into_value::{IntoValue, TryIntoValue};
19pub use range::{FloatRange, IntRange, Range};
20pub use record::Record;
21
22use crate::{
23    ast::{Bits, Boolean, CellPath, Comparison, Math, Operator, PathMember},
24    did_you_mean,
25    engine::{Closure, EngineState},
26    BlockId, Config, ShellError, Signals, Span, Type,
27};
28use chrono::{DateTime, Datelike, Duration, FixedOffset, Local, Locale, TimeZone};
29use chrono_humanize::HumanTime;
30use fancy_regex::Regex;
31use nu_utils::{
32    contains_emoji,
33    locale::{get_system_locale_string, LOCALE_OVERRIDE_ENV_VAR},
34    IgnoreCaseExt, SharedCow,
35};
36use serde::{Deserialize, Serialize};
37use std::{
38    borrow::Cow,
39    cmp::Ordering,
40    fmt::{Debug, Display, Write},
41    ops::Bound,
42    path::PathBuf,
43};
44
45/// Core structured values that pass through the pipeline in Nushell.
46// NOTE: Please do not reorder these enum cases without thinking through the
47// impact on the PartialOrd implementation and the global sort order
48#[derive(Debug, Serialize, Deserialize)]
49pub enum Value {
50    Bool {
51        val: bool,
52        /// note: spans are being refactored out of Value
53        /// please use .span() instead of matching this span value
54        #[serde(rename = "span")]
55        internal_span: Span,
56    },
57    Int {
58        val: i64,
59        /// note: spans are being refactored out of Value
60        /// please use .span() instead of matching this span value
61        #[serde(rename = "span")]
62        internal_span: Span,
63    },
64    Float {
65        val: f64,
66        /// note: spans are being refactored out of Value
67        /// please use .span() instead of matching this span value
68        #[serde(rename = "span")]
69        internal_span: Span,
70    },
71    String {
72        val: String,
73        /// note: spans are being refactored out of Value
74        /// please use .span() instead of matching this span value
75        #[serde(rename = "span")]
76        internal_span: Span,
77    },
78    Glob {
79        val: String,
80        no_expand: bool,
81        /// note: spans are being refactored out of Value
82        /// please use .span() instead of matching this span value
83        #[serde(rename = "span")]
84        internal_span: Span,
85    },
86    Filesize {
87        val: Filesize,
88        /// note: spans are being refactored out of Value
89        /// please use .span() instead of matching this span value
90        #[serde(rename = "span")]
91        internal_span: Span,
92    },
93    Duration {
94        val: i64,
95        /// note: spans are being refactored out of Value
96        /// please use .span() instead of matching this span value
97        #[serde(rename = "span")]
98        internal_span: Span,
99    },
100    Date {
101        val: DateTime<FixedOffset>,
102        /// note: spans are being refactored out of Value
103        /// please use .span() instead of matching this span value
104        #[serde(rename = "span")]
105        internal_span: Span,
106    },
107    Range {
108        val: Box<Range>,
109        /// note: spans are being refactored out of Value
110        /// please use .span() instead of matching this span value
111        #[serde(rename = "span")]
112        internal_span: Span,
113    },
114    Record {
115        val: SharedCow<Record>,
116        /// note: spans are being refactored out of Value
117        /// please use .span() instead of matching this span value
118        #[serde(rename = "span")]
119        internal_span: Span,
120    },
121    List {
122        vals: Vec<Value>,
123        /// note: spans are being refactored out of Value
124        /// please use .span() instead of matching this span value
125        #[serde(rename = "span")]
126        internal_span: Span,
127    },
128    Closure {
129        val: Box<Closure>,
130        /// note: spans are being refactored out of Value
131        /// please use .span() instead of matching this span value
132        #[serde(rename = "span")]
133        internal_span: Span,
134    },
135    Error {
136        error: Box<ShellError>,
137        /// note: spans are being refactored out of Value
138        /// please use .span() instead of matching this span value
139        #[serde(rename = "span")]
140        internal_span: Span,
141    },
142    Binary {
143        val: Vec<u8>,
144        /// note: spans are being refactored out of Value
145        /// please use .span() instead of matching this span value
146        #[serde(rename = "span")]
147        internal_span: Span,
148    },
149    CellPath {
150        val: CellPath,
151        /// note: spans are being refactored out of Value
152        /// please use .span() instead of matching this span value
153        #[serde(rename = "span")]
154        internal_span: Span,
155    },
156    Custom {
157        val: Box<dyn CustomValue>,
158        /// note: spans are being refactored out of Value
159        /// please use .span() instead of matching this span value
160        #[serde(rename = "span")]
161        internal_span: Span,
162    },
163    Nothing {
164        /// note: spans are being refactored out of Value
165        /// please use .span() instead of matching this span value
166        #[serde(rename = "span")]
167        internal_span: Span,
168    },
169}
170
171// This is to document/enforce the size of `Value` in bytes.
172// We should try to avoid increasing the size of `Value`,
173// and PRs that do so will have to change the number below so that it's noted in review.
174const _: () = assert!(std::mem::size_of::<Value>() <= 48);
175
176impl Clone for Value {
177    fn clone(&self) -> Self {
178        match self {
179            Value::Bool { val, internal_span } => Value::bool(*val, *internal_span),
180            Value::Int { val, internal_span } => Value::int(*val, *internal_span),
181            Value::Filesize { val, internal_span } => Value::Filesize {
182                val: *val,
183                internal_span: *internal_span,
184            },
185            Value::Duration { val, internal_span } => Value::Duration {
186                val: *val,
187                internal_span: *internal_span,
188            },
189            Value::Date { val, internal_span } => Value::Date {
190                val: *val,
191                internal_span: *internal_span,
192            },
193            Value::Range { val, internal_span } => Value::Range {
194                val: val.clone(),
195                internal_span: *internal_span,
196            },
197            Value::Float { val, internal_span } => Value::float(*val, *internal_span),
198            Value::String { val, internal_span } => Value::String {
199                val: val.clone(),
200                internal_span: *internal_span,
201            },
202            Value::Glob {
203                val,
204                no_expand: quoted,
205                internal_span,
206            } => Value::Glob {
207                val: val.clone(),
208                no_expand: *quoted,
209                internal_span: *internal_span,
210            },
211            Value::Record { val, internal_span } => Value::Record {
212                val: val.clone(),
213                internal_span: *internal_span,
214            },
215            Value::List {
216                vals,
217                internal_span,
218            } => Value::List {
219                vals: vals.clone(),
220                internal_span: *internal_span,
221            },
222            Value::Closure { val, internal_span } => Value::Closure {
223                val: val.clone(),
224                internal_span: *internal_span,
225            },
226            Value::Nothing { internal_span } => Value::Nothing {
227                internal_span: *internal_span,
228            },
229            Value::Error {
230                error,
231                internal_span,
232            } => Value::Error {
233                error: error.clone(),
234                internal_span: *internal_span,
235            },
236            Value::Binary { val, internal_span } => Value::Binary {
237                val: val.clone(),
238                internal_span: *internal_span,
239            },
240            Value::CellPath { val, internal_span } => Value::CellPath {
241                val: val.clone(),
242                internal_span: *internal_span,
243            },
244            Value::Custom { val, internal_span } => val.clone_value(*internal_span),
245        }
246    }
247}
248
249impl Value {
250    fn cant_convert_to<T>(&self, typ: &str) -> Result<T, ShellError> {
251        Err(ShellError::CantConvert {
252            to_type: typ.into(),
253            from_type: self.get_type().to_string(),
254            span: self.span(),
255            help: None,
256        })
257    }
258
259    /// Returns the inner `bool` value or an error if this `Value` is not a bool
260    pub fn as_bool(&self) -> Result<bool, ShellError> {
261        if let Value::Bool { val, .. } = self {
262            Ok(*val)
263        } else {
264            self.cant_convert_to("boolean")
265        }
266    }
267
268    /// Returns the inner `i64` value or an error if this `Value` is not an int
269    pub fn as_int(&self) -> Result<i64, ShellError> {
270        if let Value::Int { val, .. } = self {
271            Ok(*val)
272        } else {
273            self.cant_convert_to("int")
274        }
275    }
276
277    /// Returns the inner `f64` value or an error if this `Value` is not a float
278    pub fn as_float(&self) -> Result<f64, ShellError> {
279        if let Value::Float { val, .. } = self {
280            Ok(*val)
281        } else {
282            self.cant_convert_to("float")
283        }
284    }
285
286    /// Returns this `Value` converted to a `f64` or an error if it cannot be converted
287    ///
288    /// Only the following `Value` cases will return an `Ok` result:
289    /// - `Int`
290    /// - `Float`
291    ///
292    /// ```
293    /// # use nu_protocol::Value;
294    /// for val in Value::test_values() {
295    ///     assert_eq!(
296    ///         matches!(val, Value::Float { .. } | Value::Int { .. }),
297    ///         val.coerce_float().is_ok(),
298    ///     );
299    /// }
300    /// ```
301    pub fn coerce_float(&self) -> Result<f64, ShellError> {
302        match self {
303            Value::Float { val, .. } => Ok(*val),
304            Value::Int { val, .. } => Ok(*val as f64),
305            val => val.cant_convert_to("float"),
306        }
307    }
308
309    /// Returns the inner `i64` filesize value or an error if this `Value` is not a filesize
310    pub fn as_filesize(&self) -> Result<Filesize, ShellError> {
311        if let Value::Filesize { val, .. } = self {
312            Ok(*val)
313        } else {
314            self.cant_convert_to("filesize")
315        }
316    }
317
318    /// Returns the inner `i64` duration value or an error if this `Value` is not a duration
319    pub fn as_duration(&self) -> Result<i64, ShellError> {
320        if let Value::Duration { val, .. } = self {
321            Ok(*val)
322        } else {
323            self.cant_convert_to("duration")
324        }
325    }
326
327    /// Returns the inner [`DateTime`] value or an error if this `Value` is not a date
328    pub fn as_date(&self) -> Result<DateTime<FixedOffset>, ShellError> {
329        if let Value::Date { val, .. } = self {
330            Ok(*val)
331        } else {
332            self.cant_convert_to("datetime")
333        }
334    }
335
336    /// Returns a reference to the inner [`Range`] value or an error if this `Value` is not a range
337    pub fn as_range(&self) -> Result<Range, ShellError> {
338        if let Value::Range { val, .. } = self {
339            Ok(**val)
340        } else {
341            self.cant_convert_to("range")
342        }
343    }
344
345    /// Unwraps the inner [`Range`] value or returns an error if this `Value` is not a range
346    pub fn into_range(self) -> Result<Range, ShellError> {
347        if let Value::Range { val, .. } = self {
348            Ok(*val)
349        } else {
350            self.cant_convert_to("range")
351        }
352    }
353
354    /// Returns a reference to the inner `str` value or an error if this `Value` is not a string
355    pub fn as_str(&self) -> Result<&str, ShellError> {
356        if let Value::String { val, .. } = self {
357            Ok(val)
358        } else {
359            self.cant_convert_to("string")
360        }
361    }
362
363    /// Unwraps the inner `String` value or returns an error if this `Value` is not a string
364    pub fn into_string(self) -> Result<String, ShellError> {
365        if let Value::String { val, .. } = self {
366            Ok(val)
367        } else {
368            self.cant_convert_to("string")
369        }
370    }
371
372    /// Returns this `Value` converted to a `str` or an error if it cannot be converted
373    ///
374    /// Only the following `Value` cases will return an `Ok` result:
375    /// - `Bool`
376    /// - `Int`
377    /// - `Float`
378    /// - `String`
379    /// - `Glob`
380    /// - `Binary` (only if valid utf-8)
381    /// - `Date`
382    ///
383    /// ```
384    /// # use nu_protocol::Value;
385    /// for val in Value::test_values() {
386    ///     assert_eq!(
387    ///         matches!(
388    ///             val,
389    ///             Value::Bool { .. }
390    ///                 | Value::Int { .. }
391    ///                 | Value::Float { .. }
392    ///                 | Value::String { .. }
393    ///                 | Value::Glob { .. }
394    ///                 | Value::Binary { .. }
395    ///                 | Value::Date { .. }
396    ///         ),
397    ///         val.coerce_str().is_ok(),
398    ///     );
399    /// }
400    /// ```
401    pub fn coerce_str(&self) -> Result<Cow<str>, ShellError> {
402        match self {
403            Value::Bool { val, .. } => Ok(Cow::Owned(val.to_string())),
404            Value::Int { val, .. } => Ok(Cow::Owned(val.to_string())),
405            Value::Float { val, .. } => Ok(Cow::Owned(val.to_string())),
406            Value::String { val, .. } => Ok(Cow::Borrowed(val)),
407            Value::Glob { val, .. } => Ok(Cow::Borrowed(val)),
408            Value::Binary { val, .. } => match std::str::from_utf8(val) {
409                Ok(s) => Ok(Cow::Borrowed(s)),
410                Err(_) => self.cant_convert_to("string"),
411            },
412            Value::Date { val, .. } => Ok(Cow::Owned(
413                val.to_rfc3339_opts(chrono::SecondsFormat::Millis, true),
414            )),
415            val => val.cant_convert_to("string"),
416        }
417    }
418
419    /// Returns this `Value` converted to a `String` or an error if it cannot be converted
420    ///
421    /// # Note
422    /// This function is equivalent to `value.coerce_str().map(Cow::into_owned)`
423    /// which might allocate a new `String`.
424    ///
425    /// To avoid this allocation, prefer [`coerce_str`](Self::coerce_str)
426    /// if you do not need an owned `String`,
427    /// or [`coerce_into_string`](Self::coerce_into_string)
428    /// if you do not need to keep the original `Value` around.
429    ///
430    /// Only the following `Value` cases will return an `Ok` result:
431    /// - `Bool`
432    /// - `Int`
433    /// - `Float`
434    /// - `String`
435    /// - `Glob`
436    /// - `Binary` (only if valid utf-8)
437    /// - `Date`
438    ///
439    /// ```
440    /// # use nu_protocol::Value;
441    /// for val in Value::test_values() {
442    ///     assert_eq!(
443    ///         matches!(
444    ///             val,
445    ///             Value::Bool { .. }
446    ///                 | Value::Int { .. }
447    ///                 | Value::Float { .. }
448    ///                 | Value::String { .. }
449    ///                 | Value::Glob { .. }
450    ///                 | Value::Binary { .. }
451    ///                 | Value::Date { .. }
452    ///         ),
453    ///         val.coerce_string().is_ok(),
454    ///     );
455    /// }
456    /// ```
457    pub fn coerce_string(&self) -> Result<String, ShellError> {
458        self.coerce_str().map(Cow::into_owned)
459    }
460
461    /// Returns this `Value` converted to a `String` or an error if it cannot be converted
462    ///
463    /// Only the following `Value` cases will return an `Ok` result:
464    /// - `Bool`
465    /// - `Int`
466    /// - `Float`
467    /// - `String`
468    /// - `Glob`
469    /// - `Binary` (only if valid utf-8)
470    /// - `Date`
471    ///
472    /// ```
473    /// # use nu_protocol::Value;
474    /// for val in Value::test_values() {
475    ///     assert_eq!(
476    ///         matches!(
477    ///             val,
478    ///             Value::Bool { .. }
479    ///                 | Value::Int { .. }
480    ///                 | Value::Float { .. }
481    ///                 | Value::String { .. }
482    ///                 | Value::Glob { .. }
483    ///                 | Value::Binary { .. }
484    ///                 | Value::Date { .. }
485    ///         ),
486    ///         val.coerce_into_string().is_ok(),
487    ///     );
488    /// }
489    /// ```
490    pub fn coerce_into_string(self) -> Result<String, ShellError> {
491        let span = self.span();
492        match self {
493            Value::Bool { val, .. } => Ok(val.to_string()),
494            Value::Int { val, .. } => Ok(val.to_string()),
495            Value::Float { val, .. } => Ok(val.to_string()),
496            Value::String { val, .. } => Ok(val),
497            Value::Glob { val, .. } => Ok(val),
498            Value::Binary { val, .. } => match String::from_utf8(val) {
499                Ok(s) => Ok(s),
500                Err(err) => Value::binary(err.into_bytes(), span).cant_convert_to("string"),
501            },
502            Value::Date { val, .. } => Ok(val.to_rfc3339_opts(chrono::SecondsFormat::Millis, true)),
503            val => val.cant_convert_to("string"),
504        }
505    }
506
507    /// Returns this `Value` as a `char` or an error if it is not a single character string
508    pub fn as_char(&self) -> Result<char, ShellError> {
509        let span = self.span();
510        if let Value::String { val, .. } = self {
511            let mut chars = val.chars();
512            match (chars.next(), chars.next()) {
513                (Some(c), None) => Ok(c),
514                _ => Err(ShellError::MissingParameter {
515                    param_name: "single character separator".into(),
516                    span,
517                }),
518            }
519        } else {
520            self.cant_convert_to("char")
521        }
522    }
523
524    /// Converts this `Value` to a `PathBuf` or returns an error if it is not a string
525    pub fn to_path(&self) -> Result<PathBuf, ShellError> {
526        if let Value::String { val, .. } = self {
527            Ok(PathBuf::from(val))
528        } else {
529            self.cant_convert_to("path")
530        }
531    }
532
533    /// Returns a reference to the inner [`Record`] value or an error if this `Value` is not a record
534    pub fn as_record(&self) -> Result<&Record, ShellError> {
535        if let Value::Record { val, .. } = self {
536            Ok(val)
537        } else {
538            self.cant_convert_to("record")
539        }
540    }
541
542    /// Unwraps the inner [`Record`] value or returns an error if this `Value` is not a record
543    pub fn into_record(self) -> Result<Record, ShellError> {
544        if let Value::Record { val, .. } = self {
545            Ok(val.into_owned())
546        } else {
547            self.cant_convert_to("record")
548        }
549    }
550
551    /// Returns a reference to the inner list slice or an error if this `Value` is not a list
552    pub fn as_list(&self) -> Result<&[Value], ShellError> {
553        if let Value::List { vals, .. } = self {
554            Ok(vals)
555        } else {
556            self.cant_convert_to("list")
557        }
558    }
559
560    /// Unwraps the inner list `Vec` or returns an error if this `Value` is not a list
561    pub fn into_list(self) -> Result<Vec<Value>, ShellError> {
562        if let Value::List { vals, .. } = self {
563            Ok(vals)
564        } else {
565            self.cant_convert_to("list")
566        }
567    }
568
569    /// Returns a reference to the inner [`Closure`] value or an error if this `Value` is not a closure
570    pub fn as_closure(&self) -> Result<&Closure, ShellError> {
571        if let Value::Closure { val, .. } = self {
572            Ok(val)
573        } else {
574            self.cant_convert_to("closure")
575        }
576    }
577
578    /// Unwraps the inner [`Closure`] value or returns an error if this `Value` is not a closure
579    pub fn into_closure(self) -> Result<Closure, ShellError> {
580        if let Value::Closure { val, .. } = self {
581            Ok(*val)
582        } else {
583            self.cant_convert_to("closure")
584        }
585    }
586
587    /// Returns a reference to the inner binary slice or an error if this `Value` is not a binary value
588    pub fn as_binary(&self) -> Result<&[u8], ShellError> {
589        if let Value::Binary { val, .. } = self {
590            Ok(val)
591        } else {
592            self.cant_convert_to("binary")
593        }
594    }
595
596    /// Unwraps the inner binary `Vec` or returns an error if this `Value` is not a binary value
597    pub fn into_binary(self) -> Result<Vec<u8>, ShellError> {
598        if let Value::Binary { val, .. } = self {
599            Ok(val)
600        } else {
601            self.cant_convert_to("binary")
602        }
603    }
604
605    /// Returns this `Value` as a `u8` slice or an error if it cannot be converted
606    ///
607    /// Prefer [`coerce_into_binary`](Self::coerce_into_binary)
608    /// if you do not need to keep the original `Value` around.
609    ///
610    /// Only the following `Value` cases will return an `Ok` result:
611    /// - `Binary`
612    /// - `String`
613    ///
614    /// ```
615    /// # use nu_protocol::Value;
616    /// for val in Value::test_values() {
617    ///     assert_eq!(
618    ///         matches!(val, Value::Binary { .. } | Value::String { .. }),
619    ///         val.coerce_binary().is_ok(),
620    ///     );
621    /// }
622    /// ```
623    pub fn coerce_binary(&self) -> Result<&[u8], ShellError> {
624        match self {
625            Value::Binary { val, .. } => Ok(val),
626            Value::String { val, .. } => Ok(val.as_bytes()),
627            val => val.cant_convert_to("binary"),
628        }
629    }
630
631    /// Returns this `Value` as a `Vec<u8>` or an error if it cannot be converted
632    ///
633    /// Only the following `Value` cases will return an `Ok` result:
634    /// - `Binary`
635    /// - `String`
636    ///
637    /// ```
638    /// # use nu_protocol::Value;
639    /// for val in Value::test_values() {
640    ///     assert_eq!(
641    ///         matches!(val, Value::Binary { .. } | Value::String { .. }),
642    ///         val.coerce_into_binary().is_ok(),
643    ///     );
644    /// }
645    /// ```
646    pub fn coerce_into_binary(self) -> Result<Vec<u8>, ShellError> {
647        match self {
648            Value::Binary { val, .. } => Ok(val),
649            Value::String { val, .. } => Ok(val.into_bytes()),
650            val => val.cant_convert_to("binary"),
651        }
652    }
653
654    /// Returns a reference to the inner [`CellPath`] value or an error if this `Value` is not a cell path
655    pub fn as_cell_path(&self) -> Result<&CellPath, ShellError> {
656        if let Value::CellPath { val, .. } = self {
657            Ok(val)
658        } else {
659            self.cant_convert_to("cell path")
660        }
661    }
662
663    /// Unwraps the inner [`CellPath`] value or returns an error if this `Value` is not a cell path
664    pub fn into_cell_path(self) -> Result<CellPath, ShellError> {
665        if let Value::CellPath { val, .. } = self {
666            Ok(val)
667        } else {
668            self.cant_convert_to("cell path")
669        }
670    }
671
672    /// Interprets this `Value` as a boolean based on typical conventions for environment values.
673    ///
674    /// The following rules are used:
675    /// - Values representing `false`:
676    ///   - Empty strings or strings that equal to "false" in any case
677    ///   - The number `0` (as an integer, float or string)
678    ///   - `Nothing`
679    ///   - Explicit boolean `false`
680    /// - Values representing `true`:
681    ///   - Non-zero numbers (integer or float)
682    ///   - Non-empty strings
683    ///   - Explicit boolean `true`
684    ///
685    /// For all other, more complex variants of [`Value`], the function cannot determine a
686    /// boolean representation and returns `Err`.
687    pub fn coerce_bool(&self) -> Result<bool, ShellError> {
688        match self {
689            Value::Bool { val: false, .. } | Value::Int { val: 0, .. } | Value::Nothing { .. } => {
690                Ok(false)
691            }
692            Value::Float { val, .. } if val <= &f64::EPSILON => Ok(false),
693            Value::String { val, .. } => match val.trim().to_ascii_lowercase().as_str() {
694                "" | "0" | "false" => Ok(false),
695                _ => Ok(true),
696            },
697            Value::Bool { .. } | Value::Int { .. } | Value::Float { .. } => Ok(true),
698            _ => self.cant_convert_to("bool"),
699        }
700    }
701
702    /// Returns a reference to the inner [`CustomValue`] trait object or an error if this `Value` is not a custom value
703    pub fn as_custom_value(&self) -> Result<&dyn CustomValue, ShellError> {
704        if let Value::Custom { val, .. } = self {
705            Ok(val.as_ref())
706        } else {
707            self.cant_convert_to("custom value")
708        }
709    }
710
711    /// Unwraps the inner [`CustomValue`] trait object or returns an error if this `Value` is not a custom value
712    pub fn into_custom_value(self) -> Result<Box<dyn CustomValue>, ShellError> {
713        if let Value::Custom { val, .. } = self {
714            Ok(val)
715        } else {
716            self.cant_convert_to("custom value")
717        }
718    }
719
720    /// Get the span for the current value
721    pub fn span(&self) -> Span {
722        match self {
723            Value::Bool { internal_span, .. }
724            | Value::Int { internal_span, .. }
725            | Value::Float { internal_span, .. }
726            | Value::Filesize { internal_span, .. }
727            | Value::Duration { internal_span, .. }
728            | Value::Date { internal_span, .. }
729            | Value::Range { internal_span, .. }
730            | Value::String { internal_span, .. }
731            | Value::Glob { internal_span, .. }
732            | Value::Record { internal_span, .. }
733            | Value::List { internal_span, .. }
734            | Value::Closure { internal_span, .. }
735            | Value::Nothing { internal_span, .. }
736            | Value::Binary { internal_span, .. }
737            | Value::CellPath { internal_span, .. }
738            | Value::Custom { internal_span, .. }
739            | Value::Error { internal_span, .. } => *internal_span,
740        }
741    }
742
743    /// Set the value's span to a new span
744    pub fn set_span(&mut self, new_span: Span) {
745        match self {
746            Value::Bool { internal_span, .. }
747            | Value::Int { internal_span, .. }
748            | Value::Float { internal_span, .. }
749            | Value::Filesize { internal_span, .. }
750            | Value::Duration { internal_span, .. }
751            | Value::Date { internal_span, .. }
752            | Value::Range { internal_span, .. }
753            | Value::String { internal_span, .. }
754            | Value::Glob { internal_span, .. }
755            | Value::Record { internal_span, .. }
756            | Value::List { internal_span, .. }
757            | Value::Closure { internal_span, .. }
758            | Value::Nothing { internal_span, .. }
759            | Value::Binary { internal_span, .. }
760            | Value::CellPath { internal_span, .. }
761            | Value::Custom { internal_span, .. } => *internal_span = new_span,
762            Value::Error { .. } => (),
763        }
764    }
765
766    /// Update the value with a new span
767    pub fn with_span(mut self, new_span: Span) -> Value {
768        self.set_span(new_span);
769        self
770    }
771
772    /// Get the type of the current Value
773    pub fn get_type(&self) -> Type {
774        match self {
775            Value::Bool { .. } => Type::Bool,
776            Value::Int { .. } => Type::Int,
777            Value::Float { .. } => Type::Float,
778            Value::Filesize { .. } => Type::Filesize,
779            Value::Duration { .. } => Type::Duration,
780            Value::Date { .. } => Type::Date,
781            Value::Range { .. } => Type::Range,
782            Value::String { .. } => Type::String,
783            Value::Glob { .. } => Type::Glob,
784            Value::Record { val, .. } => {
785                Type::Record(val.iter().map(|(x, y)| (x.clone(), y.get_type())).collect())
786            }
787            Value::List { vals, .. } => {
788                let mut ty = None;
789                for val in vals {
790                    let val_ty = val.get_type();
791                    match &ty {
792                        Some(x) => {
793                            if &val_ty != x {
794                                if x.is_numeric() && val_ty.is_numeric() {
795                                    ty = Some(Type::Number)
796                                } else {
797                                    ty = Some(Type::Any);
798                                    break;
799                                }
800                            }
801                        }
802                        None => ty = Some(val_ty),
803                    }
804                }
805
806                match ty {
807                    Some(Type::Record(columns)) => Type::Table(columns),
808                    Some(ty) => Type::List(Box::new(ty)),
809                    None => Type::List(Box::new(Type::Any)),
810                }
811            }
812            Value::Nothing { .. } => Type::Nothing,
813            Value::Closure { .. } => Type::Closure,
814            Value::Error { .. } => Type::Error,
815            Value::Binary { .. } => Type::Binary,
816            Value::CellPath { .. } => Type::CellPath,
817            Value::Custom { val, .. } => Type::Custom(val.type_name().into()),
818        }
819    }
820
821    /// Determine of the [`Value`] is a [subtype](https://en.wikipedia.org/wiki/Subtyping) of `other`
822    ///
823    /// If you have a [`Value`], this method should always be used over chaining [`Value::get_type`] with [`Type::is_subtype_of`](crate::Type::is_subtype_of).
824    ///
825    /// This method is able to leverage that information encoded in a `Value` to provide more accurate
826    /// type comparison than if one were to collect the type into [`Type`](crate::Type) value with [`Value::get_type`].
827    ///
828    /// Empty lists are considered subtypes of all `list<T>` types.
829    ///
830    /// Lists of mixed records where some column is present in all record is a subtype of `table<column>`.
831    /// For example, `[{a: 1, b: 2}, {a: 1}]` is a subtype of `table<a: int>` (but not `table<a: int, b: int>`).
832    ///
833    /// See also: [`PipelineData::is_subtype_of`](crate::PipelineData::is_subtype_of)
834    pub fn is_subtype_of(&self, other: &Type) -> bool {
835        // records are structurally typed
836        let record_compatible = |val: &Value, other: &[(String, Type)]| match val {
837            Value::Record { val, .. } => other
838                .iter()
839                .all(|(key, ty)| val.get(key).is_some_and(|inner| inner.is_subtype_of(ty))),
840            _ => false,
841        };
842
843        // All cases matched explicitly to ensure this does not accidentally allocate `Type` if any composite types are introduced in the future
844        match (self, other) {
845            (_, Type::Any) => true,
846
847            // `Type` allocation for scalar types is trivial
848            (
849                Value::Bool { .. }
850                | Value::Int { .. }
851                | Value::Float { .. }
852                | Value::String { .. }
853                | Value::Glob { .. }
854                | Value::Filesize { .. }
855                | Value::Duration { .. }
856                | Value::Date { .. }
857                | Value::Range { .. }
858                | Value::Closure { .. }
859                | Value::Error { .. }
860                | Value::Binary { .. }
861                | Value::CellPath { .. }
862                | Value::Nothing { .. },
863                _,
864            ) => self.get_type().is_subtype_of(other),
865
866            // matching composite types
867            (val @ Value::Record { .. }, Type::Record(inner)) => record_compatible(val, inner),
868            (Value::List { vals, .. }, Type::List(inner)) => {
869                vals.iter().all(|val| val.is_subtype_of(inner))
870            }
871            (Value::List { vals, .. }, Type::Table(inner)) => {
872                vals.iter().all(|val| record_compatible(val, inner))
873            }
874            (Value::Custom { val, .. }, Type::Custom(inner)) => val.type_name() == **inner,
875
876            // non-matching composite types
877            (Value::Record { .. } | Value::List { .. } | Value::Custom { .. }, _) => false,
878        }
879    }
880
881    pub fn get_data_by_key(&self, name: &str) -> Option<Value> {
882        let span = self.span();
883        match self {
884            Value::Record { val, .. } => val.get(name).cloned(),
885            Value::List { vals, .. } => {
886                let out = vals
887                    .iter()
888                    .map(|item| {
889                        item.as_record()
890                            .ok()
891                            .and_then(|val| val.get(name).cloned())
892                            .unwrap_or(Value::nothing(span))
893                    })
894                    .collect::<Vec<_>>();
895
896                if !out.is_empty() {
897                    Some(Value::list(out, span))
898                } else {
899                    None
900                }
901            }
902            _ => None,
903        }
904    }
905
906    fn format_datetime<Tz: TimeZone>(&self, date_time: &DateTime<Tz>, formatter: &str) -> String
907    where
908        Tz::Offset: Display,
909    {
910        let mut formatter_buf = String::new();
911        let locale = if let Ok(l) =
912            std::env::var(LOCALE_OVERRIDE_ENV_VAR).or_else(|_| std::env::var("LC_TIME"))
913        {
914            let locale_str = l.split('.').next().unwrap_or("en_US");
915            locale_str.try_into().unwrap_or(Locale::en_US)
916        } else {
917            // LC_ALL > LC_CTYPE > LANG else en_US
918            get_system_locale_string()
919                .map(|l| l.replace('-', "_")) // `chrono::Locale` needs something like `xx_xx`, rather than `xx-xx`
920                .unwrap_or_else(|| String::from("en_US"))
921                .as_str()
922                .try_into()
923                .unwrap_or(Locale::en_US)
924        };
925        let format = date_time.format_localized(formatter, locale);
926
927        match formatter_buf.write_fmt(format_args!("{format}")) {
928            Ok(_) => (),
929            Err(_) => formatter_buf = format!("Invalid format string {}", formatter),
930        }
931        formatter_buf
932    }
933
934    /// Converts this `Value` to a string according to the given [`Config`] and separator
935    ///
936    /// This functions recurses into records and lists,
937    /// returning a string that contains the stringified form of all nested `Value`s.
938    pub fn to_expanded_string(&self, separator: &str, config: &Config) -> String {
939        let span = self.span();
940        match self {
941            Value::Bool { val, .. } => val.to_string(),
942            Value::Int { val, .. } => val.to_string(),
943            Value::Float { val, .. } => val.to_string(),
944            Value::Filesize { val, .. } => config.filesize.format(*val).to_string(),
945            Value::Duration { val, .. } => format_duration(*val),
946            Value::Date { val, .. } => match &config.datetime_format.normal {
947                Some(format) => self.format_datetime(val, format),
948                None => {
949                    format!(
950                        "{} ({})",
951                        if val.year() >= 0 {
952                            val.to_rfc2822()
953                        } else {
954                            val.to_rfc3339()
955                        },
956                        human_time_from_now(val),
957                    )
958                }
959            },
960            Value::Range { val, .. } => val.to_string(),
961            Value::String { val, .. } => val.clone(),
962            Value::Glob { val, .. } => val.clone(),
963            Value::List { vals: val, .. } => format!(
964                "[{}]",
965                val.iter()
966                    .map(|x| x.to_expanded_string(", ", config))
967                    .collect::<Vec<_>>()
968                    .join(separator)
969            ),
970            Value::Record { val, .. } => format!(
971                "{{{}}}",
972                val.iter()
973                    .map(|(x, y)| format!("{}: {}", x, y.to_expanded_string(", ", config)))
974                    .collect::<Vec<_>>()
975                    .join(separator)
976            ),
977            Value::Closure { val, .. } => format!("closure_{}", val.block_id.get()),
978            Value::Nothing { .. } => String::new(),
979            Value::Error { error, .. } => format!("{error:?}"),
980            Value::Binary { val, .. } => format!("{val:?}"),
981            Value::CellPath { val, .. } => val.to_string(),
982            // If we fail to collapse the custom value, just print <{type_name}> - failure is not
983            // that critical here
984            Value::Custom { val, .. } => val
985                .to_base_value(span)
986                .map(|val| val.to_expanded_string(separator, config))
987                .unwrap_or_else(|_| format!("<{}>", val.type_name())),
988        }
989    }
990
991    /// Converts this `Value` to a string according to the given [`Config`]
992    ///
993    /// This functions does not recurse into records and lists.
994    /// Instead, it will shorten the first list or record it finds like so:
995    /// - "[table {n} rows]"
996    /// - "[list {n} items]"
997    /// - "[record {n} fields]"
998    pub fn to_abbreviated_string(&self, config: &Config) -> String {
999        match self {
1000            Value::Date { val, .. } => match &config.datetime_format.table {
1001                Some(format) => self.format_datetime(val, format),
1002                None => human_time_from_now(val).to_string(),
1003            },
1004            Value::List { ref vals, .. } => {
1005                if !vals.is_empty() && vals.iter().all(|x| matches!(x, Value::Record { .. })) {
1006                    format!(
1007                        "[table {} row{}]",
1008                        vals.len(),
1009                        if vals.len() == 1 { "" } else { "s" }
1010                    )
1011                } else {
1012                    format!(
1013                        "[list {} item{}]",
1014                        vals.len(),
1015                        if vals.len() == 1 { "" } else { "s" }
1016                    )
1017                }
1018            }
1019            Value::Record { val, .. } => format!(
1020                "{{record {} field{}}}",
1021                val.len(),
1022                if val.len() == 1 { "" } else { "s" }
1023            ),
1024            val => val.to_expanded_string(", ", config),
1025        }
1026    }
1027
1028    /// Converts this `Value` to a string according to the given [`Config`] and separator
1029    ///
1030    /// This function adds quotes around strings,
1031    /// so that the returned string can be parsed by nushell.
1032    /// The other `Value` cases are already parsable when converted strings
1033    /// or are not yet handled by this function.
1034    ///
1035    /// This functions behaves like [`to_expanded_string`](Self::to_expanded_string)
1036    /// and will recurse into records and lists.
1037    pub fn to_parsable_string(&self, separator: &str, config: &Config) -> String {
1038        match self {
1039            // give special treatment to the simple types to make them parsable
1040            Value::String { val, .. } => format!("'{}'", val),
1041            // recurse back into this function for recursive formatting
1042            Value::List { vals: val, .. } => format!(
1043                "[{}]",
1044                val.iter()
1045                    .map(|x| x.to_parsable_string(", ", config))
1046                    .collect::<Vec<_>>()
1047                    .join(separator)
1048            ),
1049            Value::Record { val, .. } => format!(
1050                "{{{}}}",
1051                val.iter()
1052                    .map(|(x, y)| format!("{}: {}", x, y.to_parsable_string(", ", config)))
1053                    .collect::<Vec<_>>()
1054                    .join(separator)
1055            ),
1056            // defer to standard handling for types where standard representation is parsable
1057            _ => self.to_expanded_string(separator, config),
1058        }
1059    }
1060
1061    /// Convert this `Value` to a debug string
1062    ///
1063    /// In general, this function should only be used for debug purposes,
1064    /// and the resulting string should not be displayed to the user (not even in an error).
1065    pub fn to_debug_string(&self) -> String {
1066        match self {
1067            Value::String { val, .. } => {
1068                if contains_emoji(val) {
1069                    // This has to be an emoji, so let's display the code points that make it up.
1070                    format!(
1071                        "{:#?}",
1072                        Value::string(val.escape_unicode().to_string(), self.span())
1073                    )
1074                } else {
1075                    format!("{self:#?}")
1076                }
1077            }
1078            _ => format!("{self:#?}"),
1079        }
1080    }
1081
1082    /// Follow a given cell path into the value: for example accessing select elements in a stream or list
1083    pub fn follow_cell_path(
1084        self,
1085        cell_path: &[PathMember],
1086        insensitive: bool,
1087    ) -> Result<Value, ShellError> {
1088        let mut current = self;
1089
1090        for member in cell_path {
1091            match member {
1092                PathMember::Int {
1093                    val: count,
1094                    span: origin_span,
1095                    optional,
1096                } => {
1097                    // Treat a numeric path member as `select <val>`
1098                    match current {
1099                        Value::List { mut vals, .. } => {
1100                            if *count < vals.len() {
1101                                // `vals` is owned and will be dropped right after this,
1102                                // so we can `swap_remove` the value at index `count`
1103                                // without worrying about preserving order.
1104                                current = vals.swap_remove(*count);
1105                            } else if *optional {
1106                                return Ok(Value::nothing(*origin_span)); // short-circuit
1107                            } else if vals.is_empty() {
1108                                return Err(ShellError::AccessEmptyContent { span: *origin_span });
1109                            } else {
1110                                return Err(ShellError::AccessBeyondEnd {
1111                                    max_idx: vals.len() - 1,
1112                                    span: *origin_span,
1113                                });
1114                            }
1115                        }
1116                        Value::Binary { val, .. } => {
1117                            if let Some(item) = val.get(*count) {
1118                                current = Value::int(*item as i64, *origin_span);
1119                            } else if *optional {
1120                                return Ok(Value::nothing(*origin_span)); // short-circuit
1121                            } else if val.is_empty() {
1122                                return Err(ShellError::AccessEmptyContent { span: *origin_span });
1123                            } else {
1124                                return Err(ShellError::AccessBeyondEnd {
1125                                    max_idx: val.len() - 1,
1126                                    span: *origin_span,
1127                                });
1128                            }
1129                        }
1130                        Value::Range { ref val, .. } => {
1131                            if let Some(item) = val
1132                                .into_range_iter(current.span(), Signals::empty())
1133                                .nth(*count)
1134                            {
1135                                current = item;
1136                            } else if *optional {
1137                                return Ok(Value::nothing(*origin_span)); // short-circuit
1138                            } else {
1139                                return Err(ShellError::AccessBeyondEndOfStream {
1140                                    span: *origin_span,
1141                                });
1142                            }
1143                        }
1144                        Value::Custom { ref val, .. } => {
1145                            current =
1146                                match val.follow_path_int(current.span(), *count, *origin_span) {
1147                                    Ok(val) => val,
1148                                    Err(err) => {
1149                                        if *optional {
1150                                            return Ok(Value::nothing(*origin_span));
1151                                        // short-circuit
1152                                        } else {
1153                                            return Err(err);
1154                                        }
1155                                    }
1156                                };
1157                        }
1158                        Value::Nothing { .. } if *optional => {
1159                            return Ok(Value::nothing(*origin_span)); // short-circuit
1160                        }
1161                        // Records (and tables) are the only built-in which support column names,
1162                        // so only use this message for them.
1163                        Value::Record { .. } => {
1164                            return Err(ShellError::TypeMismatch {
1165                                err_message:"Can't access record values with a row index. Try specifying a column name instead".into(),
1166                                span: *origin_span,
1167                            });
1168                        }
1169                        Value::Error { error, .. } => return Err(*error),
1170                        x => {
1171                            return Err(ShellError::IncompatiblePathAccess {
1172                                type_name: format!("{}", x.get_type()),
1173                                span: *origin_span,
1174                            });
1175                        }
1176                    }
1177                }
1178                PathMember::String {
1179                    val: column_name,
1180                    span: origin_span,
1181                    optional,
1182                } => {
1183                    let span = current.span();
1184
1185                    match current {
1186                        Value::Record { mut val, .. } => {
1187                            // Make reverse iterate to avoid duplicate column leads to first value, actually last value is expected.
1188                            if let Some(found) = val.to_mut().iter_mut().rev().find(|x| {
1189                                if insensitive {
1190                                    x.0.eq_ignore_case(column_name)
1191                                } else {
1192                                    x.0 == column_name
1193                                }
1194                            }) {
1195                                current = std::mem::take(found.1);
1196                            } else if *optional {
1197                                return Ok(Value::nothing(*origin_span)); // short-circuit
1198                            } else if let Some(suggestion) =
1199                                did_you_mean(val.columns(), column_name)
1200                            {
1201                                return Err(ShellError::DidYouMean {
1202                                    suggestion,
1203                                    span: *origin_span,
1204                                });
1205                            } else {
1206                                return Err(ShellError::CantFindColumn {
1207                                    col_name: column_name.clone(),
1208                                    span: Some(*origin_span),
1209                                    src_span: span,
1210                                });
1211                            }
1212                        }
1213                        // String access of Lists always means Table access.
1214                        // Create a List which contains each matching value for contained
1215                        // records in the source list.
1216                        Value::List { vals, .. } => {
1217                            let list = vals
1218                                .into_iter()
1219                                .map(|val| {
1220                                    let val_span = val.span();
1221                                    match val {
1222                                        Value::Record { mut val, .. } => {
1223                                            if let Some(found) =
1224                                                val.to_mut().iter_mut().rev().find(|x| {
1225                                                    if insensitive {
1226                                                        x.0.eq_ignore_case(column_name)
1227                                                    } else {
1228                                                        x.0 == column_name
1229                                                    }
1230                                                })
1231                                            {
1232                                                Ok(std::mem::take(found.1))
1233                                            } else if *optional {
1234                                                Ok(Value::nothing(*origin_span))
1235                                            } else if let Some(suggestion) =
1236                                                did_you_mean(val.columns(), column_name)
1237                                            {
1238                                                Err(ShellError::DidYouMean {
1239                                                    suggestion,
1240                                                    span: *origin_span,
1241                                                })
1242                                            } else {
1243                                                Err(ShellError::CantFindColumn {
1244                                                    col_name: column_name.clone(),
1245                                                    span: Some(*origin_span),
1246                                                    src_span: val_span,
1247                                                })
1248                                            }
1249                                        }
1250                                        Value::Nothing { .. } if *optional => {
1251                                            Ok(Value::nothing(*origin_span))
1252                                        }
1253                                        _ => Err(ShellError::CantFindColumn {
1254                                            col_name: column_name.clone(),
1255                                            span: Some(*origin_span),
1256                                            src_span: val_span,
1257                                        }),
1258                                    }
1259                                })
1260                                .collect::<Result<_, _>>()?;
1261
1262                            current = Value::list(list, span);
1263                        }
1264                        Value::Custom { ref val, .. } => {
1265                            current = match val.follow_path_string(
1266                                current.span(),
1267                                column_name.clone(),
1268                                *origin_span,
1269                            ) {
1270                                Ok(val) => val,
1271                                Err(err) => {
1272                                    if *optional {
1273                                        return Ok(Value::nothing(*origin_span));
1274                                    // short-circuit
1275                                    } else {
1276                                        return Err(err);
1277                                    }
1278                                }
1279                            }
1280                        }
1281                        Value::Nothing { .. } if *optional => {
1282                            return Ok(Value::nothing(*origin_span)); // short-circuit
1283                        }
1284                        Value::Error { error, .. } => return Err(*error),
1285                        x => {
1286                            return Err(ShellError::IncompatiblePathAccess {
1287                                type_name: format!("{}", x.get_type()),
1288                                span: *origin_span,
1289                            });
1290                        }
1291                    }
1292                }
1293            }
1294        }
1295        // If a single Value::Error was produced by the above (which won't happen if nullify_errors is true), unwrap it now.
1296        // Note that Value::Errors inside Lists remain as they are, so that the rest of the list can still potentially be used.
1297        if let Value::Error { error, .. } = current {
1298            Err(*error)
1299        } else {
1300            Ok(current)
1301        }
1302    }
1303
1304    /// Follow a given cell path into the value: for example accessing select elements in a stream or list
1305    pub fn upsert_cell_path(
1306        &mut self,
1307        cell_path: &[PathMember],
1308        callback: Box<dyn FnOnce(&Value) -> Value>,
1309    ) -> Result<(), ShellError> {
1310        let orig = self.clone();
1311
1312        let new_val = callback(&orig.follow_cell_path(cell_path, false)?);
1313
1314        match new_val {
1315            Value::Error { error, .. } => Err(*error),
1316            new_val => self.upsert_data_at_cell_path(cell_path, new_val),
1317        }
1318    }
1319
1320    pub fn upsert_data_at_cell_path(
1321        &mut self,
1322        cell_path: &[PathMember],
1323        new_val: Value,
1324    ) -> Result<(), ShellError> {
1325        let v_span = self.span();
1326        if let Some((member, path)) = cell_path.split_first() {
1327            match member {
1328                PathMember::String {
1329                    val: col_name,
1330                    span,
1331                    ..
1332                } => match self {
1333                    Value::List { vals, .. } => {
1334                        for val in vals.iter_mut() {
1335                            match val {
1336                                Value::Record { val: record, .. } => {
1337                                    let record = record.to_mut();
1338                                    if let Some(val) = record.get_mut(col_name) {
1339                                        val.upsert_data_at_cell_path(path, new_val.clone())?;
1340                                    } else {
1341                                        let new_col =
1342                                            Value::with_data_at_cell_path(path, new_val.clone())?;
1343                                        record.push(col_name, new_col);
1344                                    }
1345                                }
1346                                Value::Error { error, .. } => return Err(*error.clone()),
1347                                v => {
1348                                    return Err(ShellError::CantFindColumn {
1349                                        col_name: col_name.clone(),
1350                                        span: Some(*span),
1351                                        src_span: v.span(),
1352                                    });
1353                                }
1354                            }
1355                        }
1356                    }
1357                    Value::Record { val: record, .. } => {
1358                        let record = record.to_mut();
1359                        if let Some(val) = record.get_mut(col_name) {
1360                            val.upsert_data_at_cell_path(path, new_val)?;
1361                        } else {
1362                            let new_col = Value::with_data_at_cell_path(path, new_val.clone())?;
1363                            record.push(col_name, new_col);
1364                        }
1365                    }
1366                    Value::Error { error, .. } => return Err(*error.clone()),
1367                    v => {
1368                        return Err(ShellError::CantFindColumn {
1369                            col_name: col_name.clone(),
1370                            span: Some(*span),
1371                            src_span: v.span(),
1372                        });
1373                    }
1374                },
1375                PathMember::Int {
1376                    val: row_num, span, ..
1377                } => match self {
1378                    Value::List { vals, .. } => {
1379                        if let Some(v) = vals.get_mut(*row_num) {
1380                            v.upsert_data_at_cell_path(path, new_val)?;
1381                        } else if vals.len() != *row_num {
1382                            return Err(ShellError::InsertAfterNextFreeIndex {
1383                                available_idx: vals.len(),
1384                                span: *span,
1385                            });
1386                        } else {
1387                            // If the upsert is at 1 + the end of the list, it's OK.
1388                            vals.push(Value::with_data_at_cell_path(path, new_val)?);
1389                        }
1390                    }
1391                    Value::Error { error, .. } => return Err(*error.clone()),
1392                    _ => {
1393                        return Err(ShellError::NotAList {
1394                            dst_span: *span,
1395                            src_span: v_span,
1396                        });
1397                    }
1398                },
1399            }
1400        } else {
1401            *self = new_val;
1402        }
1403        Ok(())
1404    }
1405
1406    /// Follow a given cell path into the value: for example accessing select elements in a stream or list
1407    pub fn update_cell_path<'a>(
1408        &mut self,
1409        cell_path: &[PathMember],
1410        callback: Box<dyn FnOnce(&Value) -> Value + 'a>,
1411    ) -> Result<(), ShellError> {
1412        let orig = self.clone();
1413
1414        let new_val = callback(&orig.follow_cell_path(cell_path, false)?);
1415
1416        match new_val {
1417            Value::Error { error, .. } => Err(*error),
1418            new_val => self.update_data_at_cell_path(cell_path, new_val),
1419        }
1420    }
1421
1422    pub fn update_data_at_cell_path(
1423        &mut self,
1424        cell_path: &[PathMember],
1425        new_val: Value,
1426    ) -> Result<(), ShellError> {
1427        let v_span = self.span();
1428        if let Some((member, path)) = cell_path.split_first() {
1429            match member {
1430                PathMember::String {
1431                    val: col_name,
1432                    span,
1433                    ..
1434                } => match self {
1435                    Value::List { vals, .. } => {
1436                        for val in vals.iter_mut() {
1437                            let v_span = val.span();
1438                            match val {
1439                                Value::Record { val: record, .. } => {
1440                                    if let Some(val) = record.to_mut().get_mut(col_name) {
1441                                        val.update_data_at_cell_path(path, new_val.clone())?;
1442                                    } else {
1443                                        return Err(ShellError::CantFindColumn {
1444                                            col_name: col_name.clone(),
1445                                            span: Some(*span),
1446                                            src_span: v_span,
1447                                        });
1448                                    }
1449                                }
1450                                Value::Error { error, .. } => return Err(*error.clone()),
1451                                v => {
1452                                    return Err(ShellError::CantFindColumn {
1453                                        col_name: col_name.clone(),
1454                                        span: Some(*span),
1455                                        src_span: v.span(),
1456                                    });
1457                                }
1458                            }
1459                        }
1460                    }
1461                    Value::Record { val: record, .. } => {
1462                        if let Some(val) = record.to_mut().get_mut(col_name) {
1463                            val.update_data_at_cell_path(path, new_val)?;
1464                        } else {
1465                            return Err(ShellError::CantFindColumn {
1466                                col_name: col_name.clone(),
1467                                span: Some(*span),
1468                                src_span: v_span,
1469                            });
1470                        }
1471                    }
1472                    Value::Error { error, .. } => return Err(*error.clone()),
1473                    v => {
1474                        return Err(ShellError::CantFindColumn {
1475                            col_name: col_name.clone(),
1476                            span: Some(*span),
1477                            src_span: v.span(),
1478                        });
1479                    }
1480                },
1481                PathMember::Int {
1482                    val: row_num, span, ..
1483                } => match self {
1484                    Value::List { vals, .. } => {
1485                        if let Some(v) = vals.get_mut(*row_num) {
1486                            v.update_data_at_cell_path(path, new_val)?;
1487                        } else if vals.is_empty() {
1488                            return Err(ShellError::AccessEmptyContent { span: *span });
1489                        } else {
1490                            return Err(ShellError::AccessBeyondEnd {
1491                                max_idx: vals.len() - 1,
1492                                span: *span,
1493                            });
1494                        }
1495                    }
1496                    Value::Error { error, .. } => return Err(*error.clone()),
1497                    v => {
1498                        return Err(ShellError::NotAList {
1499                            dst_span: *span,
1500                            src_span: v.span(),
1501                        });
1502                    }
1503                },
1504            }
1505        } else {
1506            *self = new_val;
1507        }
1508        Ok(())
1509    }
1510
1511    pub fn remove_data_at_cell_path(&mut self, cell_path: &[PathMember]) -> Result<(), ShellError> {
1512        match cell_path {
1513            [] => Ok(()),
1514            [member] => {
1515                let v_span = self.span();
1516                match member {
1517                    PathMember::String {
1518                        val: col_name,
1519                        span,
1520                        optional,
1521                    } => match self {
1522                        Value::List { vals, .. } => {
1523                            for val in vals.iter_mut() {
1524                                let v_span = val.span();
1525                                match val {
1526                                    Value::Record { val: record, .. } => {
1527                                        if record.to_mut().remove(col_name).is_none() && !optional {
1528                                            return Err(ShellError::CantFindColumn {
1529                                                col_name: col_name.clone(),
1530                                                span: Some(*span),
1531                                                src_span: v_span,
1532                                            });
1533                                        }
1534                                    }
1535                                    v => {
1536                                        return Err(ShellError::CantFindColumn {
1537                                            col_name: col_name.clone(),
1538                                            span: Some(*span),
1539                                            src_span: v.span(),
1540                                        });
1541                                    }
1542                                }
1543                            }
1544                            Ok(())
1545                        }
1546                        Value::Record { val: record, .. } => {
1547                            if record.to_mut().remove(col_name).is_none() && !optional {
1548                                return Err(ShellError::CantFindColumn {
1549                                    col_name: col_name.clone(),
1550                                    span: Some(*span),
1551                                    src_span: v_span,
1552                                });
1553                            }
1554                            Ok(())
1555                        }
1556                        v => Err(ShellError::CantFindColumn {
1557                            col_name: col_name.clone(),
1558                            span: Some(*span),
1559                            src_span: v.span(),
1560                        }),
1561                    },
1562                    PathMember::Int {
1563                        val: row_num,
1564                        span,
1565                        optional,
1566                    } => match self {
1567                        Value::List { vals, .. } => {
1568                            if vals.get_mut(*row_num).is_some() {
1569                                vals.remove(*row_num);
1570                                Ok(())
1571                            } else if *optional {
1572                                Ok(())
1573                            } else if vals.is_empty() {
1574                                Err(ShellError::AccessEmptyContent { span: *span })
1575                            } else {
1576                                Err(ShellError::AccessBeyondEnd {
1577                                    max_idx: vals.len() - 1,
1578                                    span: *span,
1579                                })
1580                            }
1581                        }
1582                        v => Err(ShellError::NotAList {
1583                            dst_span: *span,
1584                            src_span: v.span(),
1585                        }),
1586                    },
1587                }
1588            }
1589            [member, path @ ..] => {
1590                let v_span = self.span();
1591                match member {
1592                    PathMember::String {
1593                        val: col_name,
1594                        span,
1595                        optional,
1596                    } => match self {
1597                        Value::List { vals, .. } => {
1598                            for val in vals.iter_mut() {
1599                                let v_span = val.span();
1600                                match val {
1601                                    Value::Record { val: record, .. } => {
1602                                        if let Some(val) = record.to_mut().get_mut(col_name) {
1603                                            val.remove_data_at_cell_path(path)?;
1604                                        } else if !optional {
1605                                            return Err(ShellError::CantFindColumn {
1606                                                col_name: col_name.clone(),
1607                                                span: Some(*span),
1608                                                src_span: v_span,
1609                                            });
1610                                        }
1611                                    }
1612                                    v => {
1613                                        return Err(ShellError::CantFindColumn {
1614                                            col_name: col_name.clone(),
1615                                            span: Some(*span),
1616                                            src_span: v.span(),
1617                                        });
1618                                    }
1619                                }
1620                            }
1621                            Ok(())
1622                        }
1623                        Value::Record { val: record, .. } => {
1624                            if let Some(val) = record.to_mut().get_mut(col_name) {
1625                                val.remove_data_at_cell_path(path)?;
1626                            } else if !optional {
1627                                return Err(ShellError::CantFindColumn {
1628                                    col_name: col_name.clone(),
1629                                    span: Some(*span),
1630                                    src_span: v_span,
1631                                });
1632                            }
1633                            Ok(())
1634                        }
1635                        v => Err(ShellError::CantFindColumn {
1636                            col_name: col_name.clone(),
1637                            span: Some(*span),
1638                            src_span: v.span(),
1639                        }),
1640                    },
1641                    PathMember::Int {
1642                        val: row_num,
1643                        span,
1644                        optional,
1645                    } => match self {
1646                        Value::List { vals, .. } => {
1647                            if let Some(v) = vals.get_mut(*row_num) {
1648                                v.remove_data_at_cell_path(path)
1649                            } else if *optional {
1650                                Ok(())
1651                            } else if vals.is_empty() {
1652                                Err(ShellError::AccessEmptyContent { span: *span })
1653                            } else {
1654                                Err(ShellError::AccessBeyondEnd {
1655                                    max_idx: vals.len() - 1,
1656                                    span: *span,
1657                                })
1658                            }
1659                        }
1660                        v => Err(ShellError::NotAList {
1661                            dst_span: *span,
1662                            src_span: v.span(),
1663                        }),
1664                    },
1665                }
1666            }
1667        }
1668    }
1669    pub fn insert_data_at_cell_path(
1670        &mut self,
1671        cell_path: &[PathMember],
1672        new_val: Value,
1673        head_span: Span,
1674    ) -> Result<(), ShellError> {
1675        let v_span = self.span();
1676        if let Some((member, path)) = cell_path.split_first() {
1677            match member {
1678                PathMember::String {
1679                    val: col_name,
1680                    span,
1681                    ..
1682                } => match self {
1683                    Value::List { vals, .. } => {
1684                        for val in vals.iter_mut() {
1685                            let v_span = val.span();
1686                            match val {
1687                                Value::Record { val: record, .. } => {
1688                                    let record = record.to_mut();
1689                                    if let Some(val) = record.get_mut(col_name) {
1690                                        if path.is_empty() {
1691                                            return Err(ShellError::ColumnAlreadyExists {
1692                                                col_name: col_name.clone(),
1693                                                span: *span,
1694                                                src_span: v_span,
1695                                            });
1696                                        } else {
1697                                            val.insert_data_at_cell_path(
1698                                                path,
1699                                                new_val.clone(),
1700                                                head_span,
1701                                            )?;
1702                                        }
1703                                    } else {
1704                                        let new_col =
1705                                            Value::with_data_at_cell_path(path, new_val.clone())?;
1706                                        record.push(col_name, new_col);
1707                                    }
1708                                }
1709                                Value::Error { error, .. } => return Err(*error.clone()),
1710                                _ => {
1711                                    return Err(ShellError::UnsupportedInput {
1712                                        msg: "expected table or record".into(),
1713                                        input: format!("input type: {:?}", val.get_type()),
1714                                        msg_span: head_span,
1715                                        input_span: *span,
1716                                    });
1717                                }
1718                            }
1719                        }
1720                    }
1721                    Value::Record { val: record, .. } => {
1722                        let record = record.to_mut();
1723                        if let Some(val) = record.get_mut(col_name) {
1724                            if path.is_empty() {
1725                                return Err(ShellError::ColumnAlreadyExists {
1726                                    col_name: col_name.clone(),
1727                                    span: *span,
1728                                    src_span: v_span,
1729                                });
1730                            } else {
1731                                val.insert_data_at_cell_path(path, new_val, head_span)?;
1732                            }
1733                        } else {
1734                            let new_col = Value::with_data_at_cell_path(path, new_val)?;
1735                            record.push(col_name, new_col);
1736                        }
1737                    }
1738                    other => {
1739                        return Err(ShellError::UnsupportedInput {
1740                            msg: "table or record".into(),
1741                            input: format!("input type: {:?}", other.get_type()),
1742                            msg_span: head_span,
1743                            input_span: *span,
1744                        });
1745                    }
1746                },
1747                PathMember::Int {
1748                    val: row_num, span, ..
1749                } => match self {
1750                    Value::List { vals, .. } => {
1751                        if let Some(v) = vals.get_mut(*row_num) {
1752                            if path.is_empty() {
1753                                vals.insert(*row_num, new_val);
1754                            } else {
1755                                v.insert_data_at_cell_path(path, new_val, head_span)?;
1756                            }
1757                        } else if vals.len() != *row_num {
1758                            return Err(ShellError::InsertAfterNextFreeIndex {
1759                                available_idx: vals.len(),
1760                                span: *span,
1761                            });
1762                        } else {
1763                            // If the insert is at 1 + the end of the list, it's OK.
1764                            vals.push(Value::with_data_at_cell_path(path, new_val)?);
1765                        }
1766                    }
1767                    _ => {
1768                        return Err(ShellError::NotAList {
1769                            dst_span: *span,
1770                            src_span: v_span,
1771                        });
1772                    }
1773                },
1774            }
1775        } else {
1776            *self = new_val;
1777        }
1778        Ok(())
1779    }
1780
1781    /// Creates a new [Value] with the specified member at the specified path.
1782    /// This is used by [Value::insert_data_at_cell_path] and [Value::upsert_data_at_cell_path] whenever they have the need to insert a non-existent element
1783    fn with_data_at_cell_path(cell_path: &[PathMember], value: Value) -> Result<Value, ShellError> {
1784        if let Some((member, path)) = cell_path.split_first() {
1785            let span = value.span();
1786            match member {
1787                PathMember::String { val, .. } => Ok(Value::record(
1788                    std::iter::once((val.clone(), Value::with_data_at_cell_path(path, value)?))
1789                        .collect(),
1790                    span,
1791                )),
1792                PathMember::Int { val, .. } => {
1793                    if *val == 0usize {
1794                        Ok(Value::list(
1795                            vec![Value::with_data_at_cell_path(path, value)?],
1796                            span,
1797                        ))
1798                    } else {
1799                        Err(ShellError::InsertAfterNextFreeIndex {
1800                            available_idx: 0,
1801                            span,
1802                        })
1803                    }
1804                }
1805            }
1806        } else {
1807            Ok(value)
1808        }
1809    }
1810
1811    /// Visits all values contained within the value (including this value) with a mutable reference
1812    /// given to the closure.
1813    ///
1814    /// If the closure returns `Err`, the traversal will stop.
1815    ///
1816    /// Captures of closure values are currently visited, as they are values owned by the closure.
1817    pub fn recurse_mut<E>(
1818        &mut self,
1819        f: &mut impl FnMut(&mut Value) -> Result<(), E>,
1820    ) -> Result<(), E> {
1821        // Visit this value
1822        f(self)?;
1823        // Check for contained values
1824        match self {
1825            Value::Record { ref mut val, .. } => val
1826                .to_mut()
1827                .iter_mut()
1828                .try_for_each(|(_, rec_value)| rec_value.recurse_mut(f)),
1829            Value::List { ref mut vals, .. } => vals
1830                .iter_mut()
1831                .try_for_each(|list_value| list_value.recurse_mut(f)),
1832            // Closure captures are visited. Maybe these don't have to be if they are changed to
1833            // more opaque references.
1834            Value::Closure { ref mut val, .. } => val
1835                .captures
1836                .iter_mut()
1837                .map(|(_, captured_value)| captured_value)
1838                .try_for_each(|captured_value| captured_value.recurse_mut(f)),
1839            // All of these don't contain other values
1840            Value::Bool { .. }
1841            | Value::Int { .. }
1842            | Value::Float { .. }
1843            | Value::Filesize { .. }
1844            | Value::Duration { .. }
1845            | Value::Date { .. }
1846            | Value::Range { .. }
1847            | Value::String { .. }
1848            | Value::Glob { .. }
1849            | Value::Nothing { .. }
1850            | Value::Error { .. }
1851            | Value::Binary { .. }
1852            | Value::CellPath { .. } => Ok(()),
1853            // These could potentially contain values, but we expect the closure to handle them
1854            Value::Custom { .. } => Ok(()),
1855        }
1856    }
1857
1858    /// Check if the content is empty
1859    pub fn is_empty(&self) -> bool {
1860        match self {
1861            Value::String { val, .. } => val.is_empty(),
1862            Value::List { vals, .. } => vals.is_empty(),
1863            Value::Record { val, .. } => val.is_empty(),
1864            Value::Binary { val, .. } => val.is_empty(),
1865            Value::Nothing { .. } => true,
1866            _ => false,
1867        }
1868    }
1869
1870    pub fn is_nothing(&self) -> bool {
1871        matches!(self, Value::Nothing { .. })
1872    }
1873
1874    pub fn is_error(&self) -> bool {
1875        matches!(self, Value::Error { .. })
1876    }
1877
1878    pub fn is_true(&self) -> bool {
1879        matches!(self, Value::Bool { val: true, .. })
1880    }
1881
1882    pub fn is_false(&self) -> bool {
1883        matches!(self, Value::Bool { val: false, .. })
1884    }
1885
1886    pub fn columns(&self) -> impl Iterator<Item = &String> {
1887        let opt = match self {
1888            Value::Record { val, .. } => Some(val.columns()),
1889            _ => None,
1890        };
1891
1892        opt.into_iter().flatten()
1893    }
1894
1895    pub fn bool(val: bool, span: Span) -> Value {
1896        Value::Bool {
1897            val,
1898            internal_span: span,
1899        }
1900    }
1901
1902    pub fn int(val: i64, span: Span) -> Value {
1903        Value::Int {
1904            val,
1905            internal_span: span,
1906        }
1907    }
1908
1909    pub fn float(val: f64, span: Span) -> Value {
1910        Value::Float {
1911            val,
1912            internal_span: span,
1913        }
1914    }
1915
1916    pub fn filesize(val: impl Into<Filesize>, span: Span) -> Value {
1917        Value::Filesize {
1918            val: val.into(),
1919            internal_span: span,
1920        }
1921    }
1922
1923    pub fn duration(val: i64, span: Span) -> Value {
1924        Value::Duration {
1925            val,
1926            internal_span: span,
1927        }
1928    }
1929
1930    pub fn date(val: DateTime<FixedOffset>, span: Span) -> Value {
1931        Value::Date {
1932            val,
1933            internal_span: span,
1934        }
1935    }
1936
1937    pub fn range(val: Range, span: Span) -> Value {
1938        Value::Range {
1939            val: val.into(),
1940            internal_span: span,
1941        }
1942    }
1943
1944    pub fn string(val: impl Into<String>, span: Span) -> Value {
1945        Value::String {
1946            val: val.into(),
1947            internal_span: span,
1948        }
1949    }
1950
1951    pub fn glob(val: impl Into<String>, no_expand: bool, span: Span) -> Value {
1952        Value::Glob {
1953            val: val.into(),
1954            no_expand,
1955            internal_span: span,
1956        }
1957    }
1958
1959    pub fn record(val: Record, span: Span) -> Value {
1960        Value::Record {
1961            val: SharedCow::new(val),
1962            internal_span: span,
1963        }
1964    }
1965
1966    pub fn list(vals: Vec<Value>, span: Span) -> Value {
1967        Value::List {
1968            vals,
1969            internal_span: span,
1970        }
1971    }
1972
1973    pub fn closure(val: Closure, span: Span) -> Value {
1974        Value::Closure {
1975            val: val.into(),
1976            internal_span: span,
1977        }
1978    }
1979
1980    /// Create a new `Nothing` value
1981    pub fn nothing(span: Span) -> Value {
1982        Value::Nothing {
1983            internal_span: span,
1984        }
1985    }
1986
1987    pub fn error(error: ShellError, span: Span) -> Value {
1988        Value::Error {
1989            error: Box::new(error),
1990            internal_span: span,
1991        }
1992    }
1993
1994    pub fn binary(val: impl Into<Vec<u8>>, span: Span) -> Value {
1995        Value::Binary {
1996            val: val.into(),
1997            internal_span: span,
1998        }
1999    }
2000
2001    pub fn cell_path(val: CellPath, span: Span) -> Value {
2002        Value::CellPath {
2003            val,
2004            internal_span: span,
2005        }
2006    }
2007
2008    pub fn custom(val: Box<dyn CustomValue>, span: Span) -> Value {
2009        Value::Custom {
2010            val,
2011            internal_span: span,
2012        }
2013    }
2014
2015    /// Note: Only use this for test data, *not* live data, as it will point into unknown source
2016    /// when used in errors.
2017    pub fn test_bool(val: bool) -> Value {
2018        Value::bool(val, Span::test_data())
2019    }
2020
2021    /// Note: Only use this for test data, *not* live data, as it will point into unknown source
2022    /// when used in errors.
2023    pub fn test_int(val: i64) -> Value {
2024        Value::int(val, Span::test_data())
2025    }
2026
2027    /// Note: Only use this for test data, *not* live data, as it will point into unknown source
2028    /// when used in errors.
2029    pub fn test_float(val: f64) -> Value {
2030        Value::float(val, Span::test_data())
2031    }
2032
2033    /// Note: Only use this for test data, *not* live data, as it will point into unknown source
2034    /// when used in errors.
2035    pub fn test_filesize(val: impl Into<Filesize>) -> Value {
2036        Value::filesize(val, Span::test_data())
2037    }
2038
2039    /// Note: Only use this for test data, *not* live data, as it will point into unknown source
2040    /// when used in errors.
2041    pub fn test_duration(val: i64) -> Value {
2042        Value::duration(val, Span::test_data())
2043    }
2044
2045    /// Note: Only use this for test data, *not* live data, as it will point into unknown source
2046    /// when used in errors.
2047    pub fn test_date(val: DateTime<FixedOffset>) -> Value {
2048        Value::date(val, Span::test_data())
2049    }
2050
2051    /// Note: Only use this for test data, *not* live data, as it will point into unknown source
2052    /// when used in errors.
2053    pub fn test_range(val: Range) -> Value {
2054        Value::range(val, Span::test_data())
2055    }
2056
2057    /// Note: Only use this for test data, *not* live data, as it will point into unknown source
2058    /// when used in errors.
2059    pub fn test_string(val: impl Into<String>) -> Value {
2060        Value::string(val, Span::test_data())
2061    }
2062
2063    /// Note: Only use this for test data, *not* live data, as it will point into unknown source
2064    /// when used in errors.
2065    pub fn test_glob(val: impl Into<String>) -> Value {
2066        Value::glob(val, false, Span::test_data())
2067    }
2068
2069    /// Note: Only use this for test data, *not* live data, as it will point into unknown source
2070    /// when used in errors.
2071    pub fn test_record(val: Record) -> Value {
2072        Value::record(val, Span::test_data())
2073    }
2074
2075    /// Note: Only use this for test data, *not* live data, as it will point into unknown source
2076    /// when used in errors.
2077    pub fn test_list(vals: Vec<Value>) -> Value {
2078        Value::list(vals, Span::test_data())
2079    }
2080
2081    /// Note: Only use this for test data, *not* live data, as it will point into unknown source
2082    /// when used in errors.
2083    pub fn test_closure(val: Closure) -> Value {
2084        Value::closure(val, Span::test_data())
2085    }
2086
2087    /// Note: Only use this for test data, *not* live data, as it will point into unknown source
2088    /// when used in errors.
2089    pub fn test_nothing() -> Value {
2090        Value::nothing(Span::test_data())
2091    }
2092
2093    /// Note: Only use this for test data, *not* live data, as it will point into unknown source
2094    /// when used in errors.
2095    pub fn test_binary(val: impl Into<Vec<u8>>) -> Value {
2096        Value::binary(val, Span::test_data())
2097    }
2098
2099    /// Note: Only use this for test data, *not* live data, as it will point into unknown source
2100    /// when used in errors.
2101    pub fn test_cell_path(val: CellPath) -> Value {
2102        Value::cell_path(val, Span::test_data())
2103    }
2104
2105    /// Note: Only use this for test data, *not* live data, as it will point into unknown source
2106    /// when used in errors.
2107    pub fn test_custom_value(val: Box<dyn CustomValue>) -> Value {
2108        Value::custom(val, Span::test_data())
2109    }
2110
2111    /// Note: Only use this for test data, *not* live data,
2112    /// as it will point into unknown source when used in errors.
2113    ///
2114    /// Returns a `Vec` containing one of each value case (`Value::Int`, `Value::String`, etc.)
2115    /// except for `Value::CustomValue`.
2116    pub fn test_values() -> Vec<Value> {
2117        vec![
2118            Value::test_bool(false),
2119            Value::test_int(0),
2120            Value::test_filesize(0),
2121            Value::test_duration(0),
2122            Value::test_date(DateTime::UNIX_EPOCH.into()),
2123            Value::test_range(Range::IntRange(IntRange {
2124                start: 0,
2125                step: 1,
2126                end: Bound::Excluded(0),
2127            })),
2128            Value::test_float(0.0),
2129            Value::test_string(String::new()),
2130            Value::test_record(Record::new()),
2131            Value::test_list(Vec::new()),
2132            Value::test_closure(Closure {
2133                block_id: BlockId::new(0),
2134                captures: Vec::new(),
2135            }),
2136            Value::test_nothing(),
2137            Value::error(
2138                ShellError::NushellFailed { msg: String::new() },
2139                Span::test_data(),
2140            ),
2141            Value::test_binary(Vec::new()),
2142            Value::test_cell_path(CellPath {
2143                members: Vec::new(),
2144            }),
2145            // Value::test_custom_value(Box::new(todo!())),
2146        ]
2147    }
2148}
2149
2150impl Default for Value {
2151    fn default() -> Self {
2152        Value::Nothing {
2153            internal_span: Span::unknown(),
2154        }
2155    }
2156}
2157
2158impl PartialOrd for Value {
2159    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
2160        // Compare two floating point numbers. The decision interval for equality is dynamically
2161        // scaled as the value being compared increases in magnitude.
2162        fn compare_floats(val: f64, other: f64) -> Option<Ordering> {
2163            let prec = f64::EPSILON.max(val.abs() * f64::EPSILON);
2164
2165            if (other - val).abs() < prec {
2166                return Some(Ordering::Equal);
2167            }
2168
2169            val.partial_cmp(&other)
2170        }
2171
2172        match (self, other) {
2173            (Value::Bool { val: lhs, .. }, rhs) => match rhs {
2174                Value::Bool { val: rhs, .. } => lhs.partial_cmp(rhs),
2175                Value::Int { .. } => Some(Ordering::Less),
2176                Value::Float { .. } => Some(Ordering::Less),
2177                Value::String { .. } => Some(Ordering::Less),
2178                Value::Glob { .. } => Some(Ordering::Less),
2179                Value::Filesize { .. } => Some(Ordering::Less),
2180                Value::Duration { .. } => Some(Ordering::Less),
2181                Value::Date { .. } => Some(Ordering::Less),
2182                Value::Range { .. } => Some(Ordering::Less),
2183                Value::Record { .. } => Some(Ordering::Less),
2184                Value::List { .. } => Some(Ordering::Less),
2185                Value::Closure { .. } => Some(Ordering::Less),
2186                Value::Error { .. } => Some(Ordering::Less),
2187                Value::Binary { .. } => Some(Ordering::Less),
2188                Value::CellPath { .. } => Some(Ordering::Less),
2189                Value::Custom { .. } => Some(Ordering::Less),
2190                Value::Nothing { .. } => Some(Ordering::Less),
2191            },
2192            (Value::Int { val: lhs, .. }, rhs) => match rhs {
2193                Value::Bool { .. } => Some(Ordering::Greater),
2194                Value::Int { val: rhs, .. } => lhs.partial_cmp(rhs),
2195                Value::Float { val: rhs, .. } => compare_floats(*lhs as f64, *rhs),
2196                Value::String { .. } => Some(Ordering::Less),
2197                Value::Glob { .. } => Some(Ordering::Less),
2198                Value::Filesize { .. } => Some(Ordering::Less),
2199                Value::Duration { .. } => Some(Ordering::Less),
2200                Value::Date { .. } => Some(Ordering::Less),
2201                Value::Range { .. } => Some(Ordering::Less),
2202                Value::Record { .. } => Some(Ordering::Less),
2203                Value::List { .. } => Some(Ordering::Less),
2204                Value::Closure { .. } => Some(Ordering::Less),
2205                Value::Error { .. } => Some(Ordering::Less),
2206                Value::Binary { .. } => Some(Ordering::Less),
2207                Value::CellPath { .. } => Some(Ordering::Less),
2208                Value::Custom { .. } => Some(Ordering::Less),
2209                Value::Nothing { .. } => Some(Ordering::Less),
2210            },
2211            (Value::Float { val: lhs, .. }, rhs) => match rhs {
2212                Value::Bool { .. } => Some(Ordering::Greater),
2213                Value::Int { val: rhs, .. } => compare_floats(*lhs, *rhs as f64),
2214                Value::Float { val: rhs, .. } => compare_floats(*lhs, *rhs),
2215                Value::String { .. } => Some(Ordering::Less),
2216                Value::Glob { .. } => Some(Ordering::Less),
2217                Value::Filesize { .. } => Some(Ordering::Less),
2218                Value::Duration { .. } => Some(Ordering::Less),
2219                Value::Date { .. } => Some(Ordering::Less),
2220                Value::Range { .. } => Some(Ordering::Less),
2221                Value::Record { .. } => Some(Ordering::Less),
2222                Value::List { .. } => Some(Ordering::Less),
2223                Value::Closure { .. } => Some(Ordering::Less),
2224                Value::Error { .. } => Some(Ordering::Less),
2225                Value::Binary { .. } => Some(Ordering::Less),
2226                Value::CellPath { .. } => Some(Ordering::Less),
2227                Value::Custom { .. } => Some(Ordering::Less),
2228                Value::Nothing { .. } => Some(Ordering::Less),
2229            },
2230            (Value::String { val: lhs, .. }, rhs) => match rhs {
2231                Value::Bool { .. } => Some(Ordering::Greater),
2232                Value::Int { .. } => Some(Ordering::Greater),
2233                Value::Float { .. } => Some(Ordering::Greater),
2234                Value::String { val: rhs, .. } => lhs.partial_cmp(rhs),
2235                Value::Glob { val: rhs, .. } => lhs.partial_cmp(rhs),
2236                Value::Filesize { .. } => Some(Ordering::Less),
2237                Value::Duration { .. } => Some(Ordering::Less),
2238                Value::Date { .. } => Some(Ordering::Less),
2239                Value::Range { .. } => Some(Ordering::Less),
2240                Value::Record { .. } => Some(Ordering::Less),
2241                Value::List { .. } => Some(Ordering::Less),
2242                Value::Closure { .. } => Some(Ordering::Less),
2243                Value::Error { .. } => Some(Ordering::Less),
2244                Value::Binary { .. } => Some(Ordering::Less),
2245                Value::CellPath { .. } => Some(Ordering::Less),
2246                Value::Custom { .. } => Some(Ordering::Less),
2247                Value::Nothing { .. } => Some(Ordering::Less),
2248            },
2249            (Value::Glob { val: lhs, .. }, rhs) => match rhs {
2250                Value::Bool { .. } => Some(Ordering::Greater),
2251                Value::Int { .. } => Some(Ordering::Greater),
2252                Value::Float { .. } => Some(Ordering::Greater),
2253                Value::String { val: rhs, .. } => lhs.partial_cmp(rhs),
2254                Value::Glob { val: rhs, .. } => lhs.partial_cmp(rhs),
2255                Value::Filesize { .. } => Some(Ordering::Less),
2256                Value::Duration { .. } => Some(Ordering::Less),
2257                Value::Date { .. } => Some(Ordering::Less),
2258                Value::Range { .. } => Some(Ordering::Less),
2259                Value::Record { .. } => Some(Ordering::Less),
2260                Value::List { .. } => Some(Ordering::Less),
2261                Value::Closure { .. } => Some(Ordering::Less),
2262                Value::Error { .. } => Some(Ordering::Less),
2263                Value::Binary { .. } => Some(Ordering::Less),
2264                Value::CellPath { .. } => Some(Ordering::Less),
2265                Value::Custom { .. } => Some(Ordering::Less),
2266                Value::Nothing { .. } => Some(Ordering::Less),
2267            },
2268            (Value::Filesize { val: lhs, .. }, rhs) => match rhs {
2269                Value::Bool { .. } => Some(Ordering::Greater),
2270                Value::Int { .. } => Some(Ordering::Greater),
2271                Value::Float { .. } => Some(Ordering::Greater),
2272                Value::String { .. } => Some(Ordering::Greater),
2273                Value::Glob { .. } => Some(Ordering::Greater),
2274                Value::Filesize { val: rhs, .. } => lhs.partial_cmp(rhs),
2275                Value::Duration { .. } => Some(Ordering::Less),
2276                Value::Date { .. } => Some(Ordering::Less),
2277                Value::Range { .. } => Some(Ordering::Less),
2278                Value::Record { .. } => Some(Ordering::Less),
2279                Value::List { .. } => Some(Ordering::Less),
2280                Value::Closure { .. } => Some(Ordering::Less),
2281                Value::Error { .. } => Some(Ordering::Less),
2282                Value::Binary { .. } => Some(Ordering::Less),
2283                Value::CellPath { .. } => Some(Ordering::Less),
2284                Value::Custom { .. } => Some(Ordering::Less),
2285                Value::Nothing { .. } => Some(Ordering::Less),
2286            },
2287            (Value::Duration { val: lhs, .. }, rhs) => match rhs {
2288                Value::Bool { .. } => Some(Ordering::Greater),
2289                Value::Int { .. } => Some(Ordering::Greater),
2290                Value::Float { .. } => Some(Ordering::Greater),
2291                Value::String { .. } => Some(Ordering::Greater),
2292                Value::Glob { .. } => Some(Ordering::Greater),
2293                Value::Filesize { .. } => Some(Ordering::Greater),
2294                Value::Duration { val: rhs, .. } => lhs.partial_cmp(rhs),
2295                Value::Date { .. } => Some(Ordering::Less),
2296                Value::Range { .. } => Some(Ordering::Less),
2297                Value::Record { .. } => Some(Ordering::Less),
2298                Value::List { .. } => Some(Ordering::Less),
2299                Value::Closure { .. } => Some(Ordering::Less),
2300                Value::Error { .. } => Some(Ordering::Less),
2301                Value::Binary { .. } => Some(Ordering::Less),
2302                Value::CellPath { .. } => Some(Ordering::Less),
2303                Value::Custom { .. } => Some(Ordering::Less),
2304                Value::Nothing { .. } => Some(Ordering::Less),
2305            },
2306            (Value::Date { val: lhs, .. }, rhs) => match rhs {
2307                Value::Bool { .. } => Some(Ordering::Greater),
2308                Value::Int { .. } => Some(Ordering::Greater),
2309                Value::Float { .. } => Some(Ordering::Greater),
2310                Value::String { .. } => Some(Ordering::Greater),
2311                Value::Glob { .. } => Some(Ordering::Greater),
2312                Value::Filesize { .. } => Some(Ordering::Greater),
2313                Value::Duration { .. } => Some(Ordering::Greater),
2314                Value::Date { val: rhs, .. } => lhs.partial_cmp(rhs),
2315                Value::Range { .. } => Some(Ordering::Less),
2316                Value::Record { .. } => Some(Ordering::Less),
2317                Value::List { .. } => Some(Ordering::Less),
2318                Value::Closure { .. } => Some(Ordering::Less),
2319                Value::Error { .. } => Some(Ordering::Less),
2320                Value::Binary { .. } => Some(Ordering::Less),
2321                Value::CellPath { .. } => Some(Ordering::Less),
2322                Value::Custom { .. } => Some(Ordering::Less),
2323                Value::Nothing { .. } => Some(Ordering::Less),
2324            },
2325            (Value::Range { val: lhs, .. }, rhs) => match rhs {
2326                Value::Bool { .. } => Some(Ordering::Greater),
2327                Value::Int { .. } => Some(Ordering::Greater),
2328                Value::Float { .. } => Some(Ordering::Greater),
2329                Value::String { .. } => Some(Ordering::Greater),
2330                Value::Glob { .. } => Some(Ordering::Greater),
2331                Value::Filesize { .. } => Some(Ordering::Greater),
2332                Value::Duration { .. } => Some(Ordering::Greater),
2333                Value::Date { .. } => Some(Ordering::Greater),
2334                Value::Range { val: rhs, .. } => lhs.partial_cmp(rhs),
2335                Value::Record { .. } => Some(Ordering::Less),
2336                Value::List { .. } => Some(Ordering::Less),
2337                Value::Closure { .. } => Some(Ordering::Less),
2338                Value::Error { .. } => Some(Ordering::Less),
2339                Value::Binary { .. } => Some(Ordering::Less),
2340                Value::CellPath { .. } => Some(Ordering::Less),
2341                Value::Custom { .. } => Some(Ordering::Less),
2342                Value::Nothing { .. } => Some(Ordering::Less),
2343            },
2344            (Value::Record { val: lhs, .. }, rhs) => match rhs {
2345                Value::Bool { .. } => Some(Ordering::Greater),
2346                Value::Int { .. } => Some(Ordering::Greater),
2347                Value::Float { .. } => Some(Ordering::Greater),
2348                Value::String { .. } => Some(Ordering::Greater),
2349                Value::Glob { .. } => Some(Ordering::Greater),
2350                Value::Filesize { .. } => Some(Ordering::Greater),
2351                Value::Duration { .. } => Some(Ordering::Greater),
2352                Value::Date { .. } => Some(Ordering::Greater),
2353                Value::Range { .. } => Some(Ordering::Greater),
2354                Value::Record { val: rhs, .. } => {
2355                    // reorder cols and vals to make more logically compare.
2356                    // more general, if two record have same col and values,
2357                    // the order of cols shouldn't affect the equal property.
2358                    let mut lhs = lhs.clone().into_owned();
2359                    let mut rhs = rhs.clone().into_owned();
2360                    lhs.sort_cols();
2361                    rhs.sort_cols();
2362
2363                    // Check columns first
2364                    for (a, b) in lhs.columns().zip(rhs.columns()) {
2365                        let result = a.partial_cmp(b);
2366                        if result != Some(Ordering::Equal) {
2367                            return result;
2368                        }
2369                    }
2370                    // Then check the values
2371                    for (a, b) in lhs.values().zip(rhs.values()) {
2372                        let result = a.partial_cmp(b);
2373                        if result != Some(Ordering::Equal) {
2374                            return result;
2375                        }
2376                    }
2377                    // If all of the comparisons were equal, then lexicographical order dictates
2378                    // that the shorter sequence is less than the longer one
2379                    lhs.len().partial_cmp(&rhs.len())
2380                }
2381                Value::List { .. } => Some(Ordering::Less),
2382                Value::Closure { .. } => Some(Ordering::Less),
2383                Value::Error { .. } => Some(Ordering::Less),
2384                Value::Binary { .. } => Some(Ordering::Less),
2385                Value::CellPath { .. } => Some(Ordering::Less),
2386                Value::Custom { .. } => Some(Ordering::Less),
2387                Value::Nothing { .. } => Some(Ordering::Less),
2388            },
2389            (Value::List { vals: lhs, .. }, rhs) => match rhs {
2390                Value::Bool { .. } => Some(Ordering::Greater),
2391                Value::Int { .. } => Some(Ordering::Greater),
2392                Value::Float { .. } => Some(Ordering::Greater),
2393                Value::String { .. } => Some(Ordering::Greater),
2394                Value::Glob { .. } => Some(Ordering::Greater),
2395                Value::Filesize { .. } => Some(Ordering::Greater),
2396                Value::Duration { .. } => Some(Ordering::Greater),
2397                Value::Date { .. } => Some(Ordering::Greater),
2398                Value::Range { .. } => Some(Ordering::Greater),
2399                Value::Record { .. } => Some(Ordering::Greater),
2400                Value::List { vals: rhs, .. } => lhs.partial_cmp(rhs),
2401                Value::Closure { .. } => Some(Ordering::Less),
2402                Value::Error { .. } => Some(Ordering::Less),
2403                Value::Binary { .. } => Some(Ordering::Less),
2404                Value::CellPath { .. } => Some(Ordering::Less),
2405                Value::Custom { .. } => Some(Ordering::Less),
2406                Value::Nothing { .. } => Some(Ordering::Less),
2407            },
2408            (Value::Closure { val: lhs, .. }, rhs) => match rhs {
2409                Value::Bool { .. } => Some(Ordering::Greater),
2410                Value::Int { .. } => Some(Ordering::Greater),
2411                Value::Float { .. } => Some(Ordering::Greater),
2412                Value::String { .. } => Some(Ordering::Greater),
2413                Value::Glob { .. } => Some(Ordering::Greater),
2414                Value::Filesize { .. } => Some(Ordering::Greater),
2415                Value::Duration { .. } => Some(Ordering::Greater),
2416                Value::Date { .. } => Some(Ordering::Greater),
2417                Value::Range { .. } => Some(Ordering::Greater),
2418                Value::Record { .. } => Some(Ordering::Greater),
2419                Value::List { .. } => Some(Ordering::Greater),
2420                Value::Closure { val: rhs, .. } => lhs.block_id.partial_cmp(&rhs.block_id),
2421                Value::Error { .. } => Some(Ordering::Less),
2422                Value::Binary { .. } => Some(Ordering::Less),
2423                Value::CellPath { .. } => Some(Ordering::Less),
2424                Value::Custom { .. } => Some(Ordering::Less),
2425                Value::Nothing { .. } => Some(Ordering::Less),
2426            },
2427            (Value::Error { .. }, rhs) => match rhs {
2428                Value::Bool { .. } => Some(Ordering::Greater),
2429                Value::Int { .. } => Some(Ordering::Greater),
2430                Value::Float { .. } => Some(Ordering::Greater),
2431                Value::String { .. } => Some(Ordering::Greater),
2432                Value::Glob { .. } => Some(Ordering::Greater),
2433                Value::Filesize { .. } => Some(Ordering::Greater),
2434                Value::Duration { .. } => Some(Ordering::Greater),
2435                Value::Date { .. } => Some(Ordering::Greater),
2436                Value::Range { .. } => Some(Ordering::Greater),
2437                Value::Record { .. } => Some(Ordering::Greater),
2438                Value::List { .. } => Some(Ordering::Greater),
2439                Value::Closure { .. } => Some(Ordering::Greater),
2440                Value::Error { .. } => Some(Ordering::Equal),
2441                Value::Binary { .. } => Some(Ordering::Less),
2442                Value::CellPath { .. } => Some(Ordering::Less),
2443                Value::Custom { .. } => Some(Ordering::Less),
2444                Value::Nothing { .. } => Some(Ordering::Less),
2445            },
2446            (Value::Binary { val: lhs, .. }, rhs) => match rhs {
2447                Value::Bool { .. } => Some(Ordering::Greater),
2448                Value::Int { .. } => Some(Ordering::Greater),
2449                Value::Float { .. } => Some(Ordering::Greater),
2450                Value::String { .. } => Some(Ordering::Greater),
2451                Value::Glob { .. } => Some(Ordering::Greater),
2452                Value::Filesize { .. } => Some(Ordering::Greater),
2453                Value::Duration { .. } => Some(Ordering::Greater),
2454                Value::Date { .. } => Some(Ordering::Greater),
2455                Value::Range { .. } => Some(Ordering::Greater),
2456                Value::Record { .. } => Some(Ordering::Greater),
2457                Value::List { .. } => Some(Ordering::Greater),
2458                Value::Closure { .. } => Some(Ordering::Greater),
2459                Value::Error { .. } => Some(Ordering::Greater),
2460                Value::Binary { val: rhs, .. } => lhs.partial_cmp(rhs),
2461                Value::CellPath { .. } => Some(Ordering::Less),
2462                Value::Custom { .. } => Some(Ordering::Less),
2463                Value::Nothing { .. } => Some(Ordering::Less),
2464            },
2465            (Value::CellPath { val: lhs, .. }, rhs) => match rhs {
2466                Value::Bool { .. } => Some(Ordering::Greater),
2467                Value::Int { .. } => Some(Ordering::Greater),
2468                Value::Float { .. } => Some(Ordering::Greater),
2469                Value::String { .. } => Some(Ordering::Greater),
2470                Value::Glob { .. } => Some(Ordering::Greater),
2471                Value::Filesize { .. } => Some(Ordering::Greater),
2472                Value::Duration { .. } => Some(Ordering::Greater),
2473                Value::Date { .. } => Some(Ordering::Greater),
2474                Value::Range { .. } => Some(Ordering::Greater),
2475                Value::Record { .. } => Some(Ordering::Greater),
2476                Value::List { .. } => Some(Ordering::Greater),
2477                Value::Closure { .. } => Some(Ordering::Greater),
2478                Value::Error { .. } => Some(Ordering::Greater),
2479                Value::Binary { .. } => Some(Ordering::Greater),
2480                Value::CellPath { val: rhs, .. } => lhs.partial_cmp(rhs),
2481                Value::Custom { .. } => Some(Ordering::Less),
2482                Value::Nothing { .. } => Some(Ordering::Less),
2483            },
2484            (Value::Custom { val: lhs, .. }, rhs) => lhs.partial_cmp(rhs),
2485            (Value::Nothing { .. }, rhs) => match rhs {
2486                Value::Bool { .. } => Some(Ordering::Greater),
2487                Value::Int { .. } => Some(Ordering::Greater),
2488                Value::Float { .. } => Some(Ordering::Greater),
2489                Value::String { .. } => Some(Ordering::Greater),
2490                Value::Glob { .. } => Some(Ordering::Greater),
2491                Value::Filesize { .. } => Some(Ordering::Greater),
2492                Value::Duration { .. } => Some(Ordering::Greater),
2493                Value::Date { .. } => Some(Ordering::Greater),
2494                Value::Range { .. } => Some(Ordering::Greater),
2495                Value::Record { .. } => Some(Ordering::Greater),
2496                Value::List { .. } => Some(Ordering::Greater),
2497                Value::Closure { .. } => Some(Ordering::Greater),
2498                Value::Error { .. } => Some(Ordering::Greater),
2499                Value::Binary { .. } => Some(Ordering::Greater),
2500                Value::CellPath { .. } => Some(Ordering::Greater),
2501                Value::Custom { .. } => Some(Ordering::Greater),
2502                Value::Nothing { .. } => Some(Ordering::Equal),
2503            },
2504        }
2505    }
2506}
2507
2508impl PartialEq for Value {
2509    fn eq(&self, other: &Self) -> bool {
2510        self.partial_cmp(other).is_some_and(Ordering::is_eq)
2511    }
2512}
2513
2514impl Value {
2515    pub fn add(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
2516        match (self, rhs) {
2517            (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2518                if let Some(val) = lhs.checked_add(*rhs) {
2519                    Ok(Value::int(val, span))
2520                } else {
2521                    Err(ShellError::OperatorOverflow {
2522                        msg: "add operation overflowed".into(),
2523                        span,
2524                        help: Some("Consider using floating point values for increased range by promoting operand with 'into float'. Note: float has reduced precision!".into()),
2525                     })
2526                }
2527            }
2528            (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2529                Ok(Value::float(*lhs as f64 + *rhs, span))
2530            }
2531            (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2532                Ok(Value::float(*lhs + *rhs as f64, span))
2533            }
2534            (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2535                Ok(Value::float(lhs + rhs, span))
2536            }
2537            (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
2538                Ok(Value::string(lhs.to_string() + rhs, span))
2539            }
2540            (Value::Duration { val: lhs, .. }, Value::Date { val: rhs, .. }) => {
2541                if let Some(val) = rhs.checked_add_signed(chrono::Duration::nanoseconds(*lhs)) {
2542                    Ok(Value::date(val, span))
2543                } else {
2544                    Err(ShellError::OperatorOverflow {
2545                        msg: "addition operation overflowed".into(),
2546                        span,
2547                        help: None,
2548                    })
2549                }
2550            }
2551            (Value::Date { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
2552                if let Some(val) = lhs.checked_add_signed(chrono::Duration::nanoseconds(*rhs)) {
2553                    Ok(Value::date(val, span))
2554                } else {
2555                    Err(ShellError::OperatorOverflow {
2556                        msg: "addition operation overflowed".into(),
2557                        span,
2558                        help: None,
2559                    })
2560                }
2561            }
2562            (Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
2563                if let Some(val) = lhs.checked_add(*rhs) {
2564                    Ok(Value::duration(val, span))
2565                } else {
2566                    Err(ShellError::OperatorOverflow {
2567                        msg: "add operation overflowed".into(),
2568                        span,
2569                        help: None,
2570                    })
2571                }
2572            }
2573            (Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
2574                if let Some(val) = *lhs + *rhs {
2575                    Ok(Value::filesize(val, span))
2576                } else {
2577                    Err(ShellError::OperatorOverflow {
2578                        msg: "add operation overflowed".into(),
2579                        span,
2580                        help: None,
2581                    })
2582                }
2583            }
2584            (Value::Custom { val: lhs, .. }, rhs) => {
2585                lhs.operation(self.span(), Operator::Math(Math::Add), op, rhs)
2586            }
2587            _ => Err(operator_type_error(
2588                Operator::Math(Math::Add),
2589                op,
2590                self,
2591                rhs,
2592                |val| {
2593                    matches!(
2594                        val,
2595                        Value::Int { .. }
2596                            | Value::Float { .. }
2597                            | Value::String { .. }
2598                            | Value::Date { .. }
2599                            | Value::Duration { .. }
2600                            | Value::Filesize { .. },
2601                    )
2602                },
2603            )),
2604        }
2605    }
2606
2607    pub fn sub(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
2608        match (self, rhs) {
2609            (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2610                if let Some(val) = lhs.checked_sub(*rhs) {
2611                    Ok(Value::int(val, span))
2612                } else {
2613                    Err(ShellError::OperatorOverflow {
2614                        msg: "subtraction operation overflowed".into(),
2615                        span,
2616                        help: Some("Consider using floating point values for increased range by promoting operand with 'into float'. Note: float has reduced precision!".into()),
2617                    })
2618                }
2619            }
2620            (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2621                Ok(Value::float(*lhs as f64 - *rhs, span))
2622            }
2623            (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2624                Ok(Value::float(*lhs - *rhs as f64, span))
2625            }
2626            (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2627                Ok(Value::float(lhs - rhs, span))
2628            }
2629            (Value::Date { val: lhs, .. }, Value::Date { val: rhs, .. }) => {
2630                let result = lhs.signed_duration_since(*rhs);
2631                if let Some(v) = result.num_nanoseconds() {
2632                    Ok(Value::duration(v, span))
2633                } else {
2634                    Err(ShellError::OperatorOverflow {
2635                        msg: "subtraction operation overflowed".into(),
2636                        span,
2637                        help: None,
2638                    })
2639                }
2640            }
2641            (Value::Date { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
2642                match lhs.checked_sub_signed(chrono::Duration::nanoseconds(*rhs)) {
2643                    Some(val) => Ok(Value::date(val, span)),
2644                    _ => Err(ShellError::OperatorOverflow {
2645                        msg: "subtraction operation overflowed".into(),
2646                        span,
2647                        help: None,
2648                    }),
2649                }
2650            }
2651            (Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
2652                if let Some(val) = lhs.checked_sub(*rhs) {
2653                    Ok(Value::duration(val, span))
2654                } else {
2655                    Err(ShellError::OperatorOverflow {
2656                        msg: "subtraction operation overflowed".into(),
2657                        span,
2658                        help: None,
2659                    })
2660                }
2661            }
2662            (Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
2663                if let Some(val) = *lhs - *rhs {
2664                    Ok(Value::filesize(val, span))
2665                } else {
2666                    Err(ShellError::OperatorOverflow {
2667                        msg: "add operation overflowed".into(),
2668                        span,
2669                        help: None,
2670                    })
2671                }
2672            }
2673            (Value::Custom { val: lhs, .. }, rhs) => {
2674                lhs.operation(self.span(), Operator::Math(Math::Subtract), op, rhs)
2675            }
2676            _ => Err(operator_type_error(
2677                Operator::Math(Math::Subtract),
2678                op,
2679                self,
2680                rhs,
2681                |val| {
2682                    matches!(
2683                        val,
2684                        Value::Int { .. }
2685                            | Value::Float { .. }
2686                            | Value::Date { .. }
2687                            | Value::Duration { .. }
2688                            | Value::Filesize { .. },
2689                    )
2690                },
2691            )),
2692        }
2693    }
2694
2695    pub fn mul(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
2696        match (self, rhs) {
2697            (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2698                if let Some(val) = lhs.checked_mul(*rhs) {
2699                    Ok(Value::int(val, span))
2700                } else {
2701                    Err(ShellError::OperatorOverflow {
2702                        msg: "multiply operation overflowed".into(),
2703                        span,
2704                        help: Some("Consider using floating point values for increased range by promoting operand with 'into float'. Note: float has reduced precision!".into()),
2705                    })
2706                }
2707            }
2708            (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2709                Ok(Value::float(*lhs as f64 * *rhs, span))
2710            }
2711            (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2712                Ok(Value::float(*lhs * *rhs as f64, span))
2713            }
2714            (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2715                Ok(Value::float(lhs * rhs, span))
2716            }
2717            (Value::Int { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
2718                if let Some(val) = *lhs * *rhs {
2719                    Ok(Value::filesize(val, span))
2720                } else {
2721                    Err(ShellError::OperatorOverflow {
2722                        msg: "multiply operation overflowed".into(),
2723                        span,
2724                        help: None,
2725                    })
2726                }
2727            }
2728            (Value::Filesize { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2729                if let Some(val) = *lhs * *rhs {
2730                    Ok(Value::filesize(val, span))
2731                } else {
2732                    Err(ShellError::OperatorOverflow {
2733                        msg: "multiply operation overflowed".into(),
2734                        span,
2735                        help: None,
2736                    })
2737                }
2738            }
2739            (Value::Float { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
2740                if let Some(val) = *lhs * *rhs {
2741                    Ok(Value::filesize(val, span))
2742                } else {
2743                    Err(ShellError::OperatorOverflow {
2744                        msg: "multiply operation overflowed".into(),
2745                        span,
2746                        help: None,
2747                    })
2748                }
2749            }
2750            (Value::Filesize { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2751                if let Some(val) = *lhs * *rhs {
2752                    Ok(Value::filesize(val, span))
2753                } else {
2754                    Err(ShellError::OperatorOverflow {
2755                        msg: "multiply operation overflowed".into(),
2756                        span,
2757                        help: None,
2758                    })
2759                }
2760            }
2761            (Value::Int { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
2762                Ok(Value::duration(*lhs * *rhs, span))
2763            }
2764            (Value::Duration { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2765                Ok(Value::duration(*lhs * *rhs, span))
2766            }
2767            (Value::Duration { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2768                Ok(Value::duration((*lhs as f64 * *rhs) as i64, span))
2769            }
2770            (Value::Float { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
2771                Ok(Value::duration((*lhs * *rhs as f64) as i64, span))
2772            }
2773            (Value::Custom { val: lhs, .. }, rhs) => {
2774                lhs.operation(self.span(), Operator::Math(Math::Multiply), op, rhs)
2775            }
2776            _ => Err(operator_type_error(
2777                Operator::Math(Math::Multiply),
2778                op,
2779                self,
2780                rhs,
2781                |val| {
2782                    matches!(
2783                        val,
2784                        Value::Int { .. }
2785                            | Value::Float { .. }
2786                            | Value::Duration { .. }
2787                            | Value::Filesize { .. },
2788                    )
2789                },
2790            )),
2791        }
2792    }
2793
2794    pub fn div(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
2795        match (self, rhs) {
2796            (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2797                if *rhs == 0 {
2798                    Err(ShellError::DivisionByZero { span: op })
2799                } else {
2800                    Ok(Value::float(*lhs as f64 / *rhs as f64, span))
2801                }
2802            }
2803            (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2804                if *rhs != 0.0 {
2805                    Ok(Value::float(*lhs as f64 / *rhs, span))
2806                } else {
2807                    Err(ShellError::DivisionByZero { span: op })
2808                }
2809            }
2810            (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2811                if *rhs != 0 {
2812                    Ok(Value::float(*lhs / *rhs as f64, span))
2813                } else {
2814                    Err(ShellError::DivisionByZero { span: op })
2815                }
2816            }
2817            (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2818                if *rhs != 0.0 {
2819                    Ok(Value::float(lhs / rhs, span))
2820                } else {
2821                    Err(ShellError::DivisionByZero { span: op })
2822                }
2823            }
2824            (Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
2825                if *rhs == Filesize::ZERO {
2826                    Err(ShellError::DivisionByZero { span: op })
2827                } else {
2828                    Ok(Value::float(lhs.get() as f64 / rhs.get() as f64, span))
2829                }
2830            }
2831            (Value::Filesize { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2832                if let Some(val) = lhs.get().checked_div(*rhs) {
2833                    Ok(Value::filesize(val, span))
2834                } else if *rhs == 0 {
2835                    Err(ShellError::DivisionByZero { span: op })
2836                } else {
2837                    Err(ShellError::OperatorOverflow {
2838                        msg: "division operation overflowed".into(),
2839                        span,
2840                        help: None,
2841                    })
2842                }
2843            }
2844            (Value::Filesize { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2845                if *rhs != 0.0 {
2846                    if let Ok(val) = Filesize::try_from(lhs.get() as f64 / rhs) {
2847                        Ok(Value::filesize(val, span))
2848                    } else {
2849                        Err(ShellError::OperatorOverflow {
2850                            msg: "division operation overflowed".into(),
2851                            span,
2852                            help: None,
2853                        })
2854                    }
2855                } else {
2856                    Err(ShellError::DivisionByZero { span: op })
2857                }
2858            }
2859            (Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
2860                if *rhs == 0 {
2861                    Err(ShellError::DivisionByZero { span: op })
2862                } else {
2863                    Ok(Value::float(*lhs as f64 / *rhs as f64, span))
2864                }
2865            }
2866            (Value::Duration { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2867                if let Some(val) = lhs.checked_div(*rhs) {
2868                    Ok(Value::duration(val, span))
2869                } else if *rhs == 0 {
2870                    Err(ShellError::DivisionByZero { span: op })
2871                } else {
2872                    Err(ShellError::OperatorOverflow {
2873                        msg: "division operation overflowed".into(),
2874                        span,
2875                        help: None,
2876                    })
2877                }
2878            }
2879            (Value::Duration { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2880                if *rhs != 0.0 {
2881                    let val = *lhs as f64 / rhs;
2882                    if i64::MIN as f64 <= val && val <= i64::MAX as f64 {
2883                        Ok(Value::duration(val as i64, span))
2884                    } else {
2885                        Err(ShellError::OperatorOverflow {
2886                            msg: "division operation overflowed".into(),
2887                            span,
2888                            help: None,
2889                        })
2890                    }
2891                } else {
2892                    Err(ShellError::DivisionByZero { span: op })
2893                }
2894            }
2895            (Value::Custom { val: lhs, .. }, rhs) => {
2896                lhs.operation(self.span(), Operator::Math(Math::Divide), op, rhs)
2897            }
2898            _ => Err(operator_type_error(
2899                Operator::Math(Math::Divide),
2900                op,
2901                self,
2902                rhs,
2903                |val| {
2904                    matches!(
2905                        val,
2906                        Value::Int { .. }
2907                            | Value::Float { .. }
2908                            | Value::Duration { .. }
2909                            | Value::Filesize { .. },
2910                    )
2911                },
2912            )),
2913        }
2914    }
2915
2916    pub fn floor_div(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
2917        // Taken from the unstable `div_floor` function in the std library.
2918        fn checked_div_floor_i64(dividend: i64, divisor: i64) -> Option<i64> {
2919            let quotient = dividend.checked_div(divisor)?;
2920            let remainder = dividend.checked_rem(divisor)?;
2921            if (remainder > 0 && divisor < 0) || (remainder < 0 && divisor > 0) {
2922                // Note that `quotient - 1` cannot overflow, because:
2923                //     `quotient` would have to be `i64::MIN`
2924                //     => `divisor` would have to be `1`
2925                //     => `remainder` would have to be `0`
2926                // But `remainder == 0` is excluded from the check above.
2927                Some(quotient - 1)
2928            } else {
2929                Some(quotient)
2930            }
2931        }
2932
2933        fn checked_div_floor_f64(dividend: f64, divisor: f64) -> Option<f64> {
2934            if divisor == 0.0 {
2935                None
2936            } else {
2937                Some((dividend / divisor).floor())
2938            }
2939        }
2940
2941        match (self, rhs) {
2942            (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2943                if let Some(val) = checked_div_floor_i64(*lhs, *rhs) {
2944                    Ok(Value::int(val, span))
2945                } else if *rhs == 0 {
2946                    Err(ShellError::DivisionByZero { span: op })
2947                } else {
2948                    Err(ShellError::OperatorOverflow {
2949                        msg: "division operation overflowed".into(),
2950                        span,
2951                        help: None,
2952                    })
2953                }
2954            }
2955            (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2956                if let Some(val) = checked_div_floor_f64(*lhs as f64, *rhs) {
2957                    Ok(Value::float(val, span))
2958                } else {
2959                    Err(ShellError::DivisionByZero { span: op })
2960                }
2961            }
2962            (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2963                if let Some(val) = checked_div_floor_f64(*lhs, *rhs as f64) {
2964                    Ok(Value::float(val, span))
2965                } else {
2966                    Err(ShellError::DivisionByZero { span: op })
2967                }
2968            }
2969            (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2970                if let Some(val) = checked_div_floor_f64(*lhs, *rhs) {
2971                    Ok(Value::float(val, span))
2972                } else {
2973                    Err(ShellError::DivisionByZero { span: op })
2974                }
2975            }
2976            (Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
2977                if let Some(val) = checked_div_floor_i64(lhs.get(), rhs.get()) {
2978                    Ok(Value::int(val, span))
2979                } else if *rhs == Filesize::ZERO {
2980                    Err(ShellError::DivisionByZero { span: op })
2981                } else {
2982                    Err(ShellError::OperatorOverflow {
2983                        msg: "division operation overflowed".into(),
2984                        span,
2985                        help: None,
2986                    })
2987                }
2988            }
2989            (Value::Filesize { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2990                if let Some(val) = checked_div_floor_i64(lhs.get(), *rhs) {
2991                    Ok(Value::filesize(val, span))
2992                } else if *rhs == 0 {
2993                    Err(ShellError::DivisionByZero { span: op })
2994                } else {
2995                    Err(ShellError::OperatorOverflow {
2996                        msg: "division operation overflowed".into(),
2997                        span,
2998                        help: None,
2999                    })
3000                }
3001            }
3002            (Value::Filesize { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3003                if let Some(val) = checked_div_floor_f64(lhs.get() as f64, *rhs) {
3004                    if let Ok(val) = Filesize::try_from(val) {
3005                        Ok(Value::filesize(val, span))
3006                    } else {
3007                        Err(ShellError::OperatorOverflow {
3008                            msg: "division operation overflowed".into(),
3009                            span,
3010                            help: None,
3011                        })
3012                    }
3013                } else {
3014                    Err(ShellError::DivisionByZero { span: op })
3015                }
3016            }
3017            (Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
3018                if let Some(val) = checked_div_floor_i64(*lhs, *rhs) {
3019                    Ok(Value::int(val, span))
3020                } else if *rhs == 0 {
3021                    Err(ShellError::DivisionByZero { span: op })
3022                } else {
3023                    Err(ShellError::OperatorOverflow {
3024                        msg: "division operation overflowed".into(),
3025                        span,
3026                        help: None,
3027                    })
3028                }
3029            }
3030            (Value::Duration { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3031                if let Some(val) = checked_div_floor_i64(*lhs, *rhs) {
3032                    Ok(Value::duration(val, span))
3033                } else if *rhs == 0 {
3034                    Err(ShellError::DivisionByZero { span: op })
3035                } else {
3036                    Err(ShellError::OperatorOverflow {
3037                        msg: "division operation overflowed".into(),
3038                        span,
3039                        help: None,
3040                    })
3041                }
3042            }
3043            (Value::Duration { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3044                if let Some(val) = checked_div_floor_f64(*lhs as f64, *rhs) {
3045                    if i64::MIN as f64 <= val && val <= i64::MAX as f64 {
3046                        Ok(Value::duration(val as i64, span))
3047                    } else {
3048                        Err(ShellError::OperatorOverflow {
3049                            msg: "division operation overflowed".into(),
3050                            span,
3051                            help: None,
3052                        })
3053                    }
3054                } else {
3055                    Err(ShellError::DivisionByZero { span: op })
3056                }
3057            }
3058            (Value::Custom { val: lhs, .. }, rhs) => {
3059                lhs.operation(self.span(), Operator::Math(Math::FloorDivide), op, rhs)
3060            }
3061            _ => Err(operator_type_error(
3062                Operator::Math(Math::FloorDivide),
3063                op,
3064                self,
3065                rhs,
3066                |val| {
3067                    matches!(
3068                        val,
3069                        Value::Int { .. }
3070                            | Value::Float { .. }
3071                            | Value::Duration { .. }
3072                            | Value::Filesize { .. },
3073                    )
3074                },
3075            )),
3076        }
3077    }
3078
3079    pub fn modulo(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3080        // Based off the unstable `div_floor` function in the std library.
3081        fn checked_mod_i64(dividend: i64, divisor: i64) -> Option<i64> {
3082            let remainder = dividend.checked_rem(divisor)?;
3083            if (remainder > 0 && divisor < 0) || (remainder < 0 && divisor > 0) {
3084                // Note that `remainder + divisor` cannot overflow, because `remainder` and
3085                // `divisor` have opposite signs.
3086                Some(remainder + divisor)
3087            } else {
3088                Some(remainder)
3089            }
3090        }
3091
3092        fn checked_mod_f64(dividend: f64, divisor: f64) -> Option<f64> {
3093            if divisor == 0.0 {
3094                None
3095            } else {
3096                let remainder = dividend % divisor;
3097                if (remainder > 0.0 && divisor < 0.0) || (remainder < 0.0 && divisor > 0.0) {
3098                    Some(remainder + divisor)
3099                } else {
3100                    Some(remainder)
3101                }
3102            }
3103        }
3104
3105        match (self, rhs) {
3106            (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3107                if let Some(val) = checked_mod_i64(*lhs, *rhs) {
3108                    Ok(Value::int(val, span))
3109                } else if *rhs == 0 {
3110                    Err(ShellError::DivisionByZero { span: op })
3111                } else {
3112                    Err(ShellError::OperatorOverflow {
3113                        msg: "modulo operation overflowed".into(),
3114                        span,
3115                        help: None,
3116                    })
3117                }
3118            }
3119            (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3120                if let Some(val) = checked_mod_f64(*lhs as f64, *rhs) {
3121                    Ok(Value::float(val, span))
3122                } else {
3123                    Err(ShellError::DivisionByZero { span: op })
3124                }
3125            }
3126            (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3127                if let Some(val) = checked_mod_f64(*lhs, *rhs as f64) {
3128                    Ok(Value::float(val, span))
3129                } else {
3130                    Err(ShellError::DivisionByZero { span: op })
3131                }
3132            }
3133            (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3134                if let Some(val) = checked_mod_f64(*lhs, *rhs) {
3135                    Ok(Value::float(val, span))
3136                } else {
3137                    Err(ShellError::DivisionByZero { span: op })
3138                }
3139            }
3140            (Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
3141                if let Some(val) = checked_mod_i64(lhs.get(), rhs.get()) {
3142                    Ok(Value::filesize(val, span))
3143                } else if *rhs == Filesize::ZERO {
3144                    Err(ShellError::DivisionByZero { span: op })
3145                } else {
3146                    Err(ShellError::OperatorOverflow {
3147                        msg: "modulo operation overflowed".into(),
3148                        span,
3149                        help: None,
3150                    })
3151                }
3152            }
3153            (Value::Filesize { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3154                if let Some(val) = checked_mod_i64(lhs.get(), *rhs) {
3155                    Ok(Value::filesize(val, span))
3156                } else if *rhs == 0 {
3157                    Err(ShellError::DivisionByZero { span: op })
3158                } else {
3159                    Err(ShellError::OperatorOverflow {
3160                        msg: "modulo operation overflowed".into(),
3161                        span,
3162                        help: None,
3163                    })
3164                }
3165            }
3166            (Value::Filesize { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3167                if let Some(val) = checked_mod_f64(lhs.get() as f64, *rhs) {
3168                    if let Ok(val) = Filesize::try_from(val) {
3169                        Ok(Value::filesize(val, span))
3170                    } else {
3171                        Err(ShellError::OperatorOverflow {
3172                            msg: "modulo operation overflowed".into(),
3173                            span,
3174                            help: None,
3175                        })
3176                    }
3177                } else {
3178                    Err(ShellError::DivisionByZero { span: op })
3179                }
3180            }
3181            (Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
3182                if let Some(val) = checked_mod_i64(*lhs, *rhs) {
3183                    Ok(Value::duration(val, span))
3184                } else if *rhs == 0 {
3185                    Err(ShellError::DivisionByZero { span: op })
3186                } else {
3187                    Err(ShellError::OperatorOverflow {
3188                        msg: "division operation overflowed".into(),
3189                        span,
3190                        help: None,
3191                    })
3192                }
3193            }
3194            (Value::Duration { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3195                if let Some(val) = checked_mod_i64(*lhs, *rhs) {
3196                    Ok(Value::duration(val, span))
3197                } else if *rhs == 0 {
3198                    Err(ShellError::DivisionByZero { span: op })
3199                } else {
3200                    Err(ShellError::OperatorOverflow {
3201                        msg: "division operation overflowed".into(),
3202                        span,
3203                        help: None,
3204                    })
3205                }
3206            }
3207            (Value::Duration { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3208                if let Some(val) = checked_mod_f64(*lhs as f64, *rhs) {
3209                    if i64::MIN as f64 <= val && val <= i64::MAX as f64 {
3210                        Ok(Value::duration(val as i64, span))
3211                    } else {
3212                        Err(ShellError::OperatorOverflow {
3213                            msg: "division operation overflowed".into(),
3214                            span,
3215                            help: None,
3216                        })
3217                    }
3218                } else {
3219                    Err(ShellError::DivisionByZero { span: op })
3220                }
3221            }
3222            (Value::Custom { val: lhs, .. }, rhs) => {
3223                lhs.operation(span, Operator::Math(Math::Modulo), op, rhs)
3224            }
3225            _ => Err(operator_type_error(
3226                Operator::Math(Math::Modulo),
3227                op,
3228                self,
3229                rhs,
3230                |val| {
3231                    matches!(
3232                        val,
3233                        Value::Int { .. }
3234                            | Value::Float { .. }
3235                            | Value::Duration { .. }
3236                            | Value::Filesize { .. },
3237                    )
3238                },
3239            )),
3240        }
3241    }
3242
3243    pub fn pow(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3244        match (self, rhs) {
3245            (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3246                if let Some(val) = lhs.checked_pow(*rhs as u32) {
3247                    Ok(Value::int(val, span))
3248                } else {
3249                    Err(ShellError::OperatorOverflow {
3250                        msg: "pow operation overflowed".into(),
3251                        span,
3252                        help: Some("Consider using floating point values for increased range by promoting operand with 'into float'. Note: float has reduced precision!".into()),
3253                    })
3254                }
3255            }
3256            (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3257                Ok(Value::float((*lhs as f64).powf(*rhs), span))
3258            }
3259            (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3260                Ok(Value::float(lhs.powf(*rhs as f64), span))
3261            }
3262            (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3263                Ok(Value::float(lhs.powf(*rhs), span))
3264            }
3265            (Value::Custom { val: lhs, .. }, rhs) => {
3266                lhs.operation(span, Operator::Math(Math::Pow), op, rhs)
3267            }
3268            _ => Err(operator_type_error(
3269                Operator::Math(Math::Pow),
3270                op,
3271                self,
3272                rhs,
3273                |val| matches!(val, Value::Int { .. } | Value::Float { .. }),
3274            )),
3275        }
3276    }
3277
3278    pub fn concat(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3279        match (self, rhs) {
3280            (Value::List { vals: lhs, .. }, Value::List { vals: rhs, .. }) => {
3281                Ok(Value::list([lhs.as_slice(), rhs.as_slice()].concat(), span))
3282            }
3283            (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
3284                Ok(Value::string([lhs.as_str(), rhs.as_str()].join(""), span))
3285            }
3286            (Value::Binary { val: lhs, .. }, Value::Binary { val: rhs, .. }) => Ok(Value::binary(
3287                [lhs.as_slice(), rhs.as_slice()].concat(),
3288                span,
3289            )),
3290            (Value::Custom { val: lhs, .. }, rhs) => {
3291                lhs.operation(self.span(), Operator::Math(Math::Concatenate), op, rhs)
3292            }
3293            _ => {
3294                let help = if matches!(self, Value::List { .. })
3295                    || matches!(rhs, Value::List { .. })
3296                {
3297                    Some("if you meant to append a value to a list or a record to a table, use the `append` command or wrap the value in a list. For example: `$list ++ $value` should be `$list ++ [$value]` or `$list | append $value`.")
3298                } else {
3299                    None
3300                };
3301                let is_supported = |val: &Value| {
3302                    matches!(
3303                        val,
3304                        Value::List { .. }
3305                            | Value::String { .. }
3306                            | Value::Binary { .. }
3307                            | Value::Custom { .. }
3308                    )
3309                };
3310                Err(match (is_supported(self), is_supported(rhs)) {
3311                    (true, true) => ShellError::OperatorIncompatibleTypes {
3312                        op: Operator::Math(Math::Concatenate),
3313                        lhs: self.get_type(),
3314                        rhs: rhs.get_type(),
3315                        op_span: op,
3316                        lhs_span: self.span(),
3317                        rhs_span: rhs.span(),
3318                        help,
3319                    },
3320                    (true, false) => ShellError::OperatorUnsupportedType {
3321                        op: Operator::Math(Math::Concatenate),
3322                        unsupported: rhs.get_type(),
3323                        op_span: op,
3324                        unsupported_span: rhs.span(),
3325                        help,
3326                    },
3327                    (false, _) => ShellError::OperatorUnsupportedType {
3328                        op: Operator::Math(Math::Concatenate),
3329                        unsupported: self.get_type(),
3330                        op_span: op,
3331                        unsupported_span: self.span(),
3332                        help,
3333                    },
3334                })
3335            }
3336        }
3337    }
3338
3339    pub fn lt(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3340        if let (Value::Custom { val: lhs, .. }, rhs) = (self, rhs) {
3341            return lhs.operation(
3342                self.span(),
3343                Operator::Comparison(Comparison::LessThan),
3344                op,
3345                rhs,
3346            );
3347        }
3348
3349        if matches!(self, Value::Nothing { .. }) || matches!(rhs, Value::Nothing { .. }) {
3350            return Ok(Value::nothing(span));
3351        }
3352
3353        if !type_compatible(self.get_type(), rhs.get_type()) {
3354            return Err(operator_type_error(
3355                Operator::Comparison(Comparison::LessThan),
3356                op,
3357                self,
3358                rhs,
3359                |val| {
3360                    matches!(
3361                        val,
3362                        Value::Int { .. }
3363                            | Value::Float { .. }
3364                            | Value::String { .. }
3365                            | Value::Filesize { .. }
3366                            | Value::Duration { .. }
3367                            | Value::Date { .. }
3368                            | Value::Bool { .. }
3369                            | Value::Nothing { .. }
3370                    )
3371                },
3372            ));
3373        }
3374
3375        Ok(Value::bool(
3376            matches!(self.partial_cmp(rhs), Some(Ordering::Less)),
3377            span,
3378        ))
3379    }
3380
3381    pub fn lte(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3382        if let (Value::Custom { val: lhs, .. }, rhs) = (self, rhs) {
3383            return lhs.operation(
3384                self.span(),
3385                Operator::Comparison(Comparison::LessThanOrEqual),
3386                op,
3387                rhs,
3388            );
3389        }
3390
3391        if matches!(self, Value::Nothing { .. }) || matches!(rhs, Value::Nothing { .. }) {
3392            return Ok(Value::nothing(span));
3393        }
3394
3395        if !type_compatible(self.get_type(), rhs.get_type()) {
3396            return Err(operator_type_error(
3397                Operator::Comparison(Comparison::LessThanOrEqual),
3398                op,
3399                self,
3400                rhs,
3401                |val| {
3402                    matches!(
3403                        val,
3404                        Value::Int { .. }
3405                            | Value::Float { .. }
3406                            | Value::String { .. }
3407                            | Value::Filesize { .. }
3408                            | Value::Duration { .. }
3409                            | Value::Date { .. }
3410                            | Value::Bool { .. }
3411                            | Value::Nothing { .. }
3412                    )
3413                },
3414            ));
3415        }
3416
3417        Ok(Value::bool(
3418            matches!(
3419                self.partial_cmp(rhs),
3420                Some(Ordering::Less | Ordering::Equal)
3421            ),
3422            span,
3423        ))
3424    }
3425
3426    pub fn gt(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3427        if let (Value::Custom { val: lhs, .. }, rhs) = (self, rhs) {
3428            return lhs.operation(
3429                self.span(),
3430                Operator::Comparison(Comparison::GreaterThan),
3431                op,
3432                rhs,
3433            );
3434        }
3435
3436        if matches!(self, Value::Nothing { .. }) || matches!(rhs, Value::Nothing { .. }) {
3437            return Ok(Value::nothing(span));
3438        }
3439
3440        if !type_compatible(self.get_type(), rhs.get_type()) {
3441            return Err(operator_type_error(
3442                Operator::Comparison(Comparison::GreaterThan),
3443                op,
3444                self,
3445                rhs,
3446                |val| {
3447                    matches!(
3448                        val,
3449                        Value::Int { .. }
3450                            | Value::Float { .. }
3451                            | Value::String { .. }
3452                            | Value::Filesize { .. }
3453                            | Value::Duration { .. }
3454                            | Value::Date { .. }
3455                            | Value::Bool { .. }
3456                            | Value::Nothing { .. }
3457                    )
3458                },
3459            ));
3460        }
3461
3462        Ok(Value::bool(
3463            matches!(self.partial_cmp(rhs), Some(Ordering::Greater)),
3464            span,
3465        ))
3466    }
3467
3468    pub fn gte(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3469        if let (Value::Custom { val: lhs, .. }, rhs) = (self, rhs) {
3470            return lhs.operation(
3471                self.span(),
3472                Operator::Comparison(Comparison::GreaterThanOrEqual),
3473                op,
3474                rhs,
3475            );
3476        }
3477
3478        if matches!(self, Value::Nothing { .. }) || matches!(rhs, Value::Nothing { .. }) {
3479            return Ok(Value::nothing(span));
3480        }
3481
3482        if !type_compatible(self.get_type(), rhs.get_type()) {
3483            return Err(operator_type_error(
3484                Operator::Comparison(Comparison::GreaterThanOrEqual),
3485                op,
3486                self,
3487                rhs,
3488                |val| {
3489                    matches!(
3490                        val,
3491                        Value::Int { .. }
3492                            | Value::Float { .. }
3493                            | Value::String { .. }
3494                            | Value::Filesize { .. }
3495                            | Value::Duration { .. }
3496                            | Value::Date { .. }
3497                            | Value::Bool { .. }
3498                            | Value::Nothing { .. }
3499                    )
3500                },
3501            ));
3502        }
3503
3504        Ok(Value::bool(
3505            matches!(
3506                self.partial_cmp(rhs),
3507                Some(Ordering::Greater | Ordering::Equal)
3508            ),
3509            span,
3510        ))
3511    }
3512
3513    pub fn eq(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3514        if let (Value::Custom { val: lhs, .. }, rhs) = (self, rhs) {
3515            return lhs.operation(
3516                self.span(),
3517                Operator::Comparison(Comparison::Equal),
3518                op,
3519                rhs,
3520            );
3521        }
3522
3523        Ok(Value::bool(
3524            matches!(self.partial_cmp(rhs), Some(Ordering::Equal)),
3525            span,
3526        ))
3527    }
3528
3529    pub fn ne(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3530        if let (Value::Custom { val: lhs, .. }, rhs) = (self, rhs) {
3531            return lhs.operation(
3532                self.span(),
3533                Operator::Comparison(Comparison::NotEqual),
3534                op,
3535                rhs,
3536            );
3537        }
3538
3539        Ok(Value::bool(
3540            !matches!(self.partial_cmp(rhs), Some(Ordering::Equal)),
3541            span,
3542        ))
3543    }
3544
3545    pub fn r#in(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3546        match (self, rhs) {
3547            (lhs, Value::Range { val: rhs, .. }) => Ok(Value::bool(rhs.contains(lhs), span)),
3548            (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
3549                Ok(Value::bool(rhs.contains(lhs), span))
3550            }
3551            (lhs, Value::List { vals: rhs, .. }) => Ok(Value::bool(rhs.contains(lhs), span)),
3552            (Value::String { val: lhs, .. }, Value::Record { val: rhs, .. }) => {
3553                Ok(Value::bool(rhs.contains(lhs), span))
3554            }
3555            (Value::String { .. } | Value::Int { .. }, Value::CellPath { val: rhs, .. }) => {
3556                let val = rhs.members.iter().any(|member| match (self, member) {
3557                    (Value::Int { val: lhs, .. }, PathMember::Int { val: rhs, .. }) => {
3558                        *lhs == *rhs as i64
3559                    }
3560                    (Value::String { val: lhs, .. }, PathMember::String { val: rhs, .. }) => {
3561                        lhs == rhs
3562                    }
3563                    (Value::String { .. }, PathMember::Int { .. })
3564                    | (Value::Int { .. }, PathMember::String { .. }) => false,
3565                    _ => unreachable!(
3566                        "outer match arm ensures `self` is either a `String` or `Int` variant"
3567                    ),
3568                });
3569
3570                Ok(Value::bool(val, span))
3571            }
3572            (Value::CellPath { val: lhs, .. }, Value::CellPath { val: rhs, .. }) => {
3573                Ok(Value::bool(
3574                    rhs.members
3575                        .windows(lhs.members.len())
3576                        .any(|member_window| member_window == rhs.members),
3577                    span,
3578                ))
3579            }
3580            (Value::Custom { val: lhs, .. }, rhs) => {
3581                lhs.operation(self.span(), Operator::Comparison(Comparison::In), op, rhs)
3582            }
3583            (lhs, rhs) => Err(
3584                if matches!(
3585                    rhs,
3586                    Value::List { .. }
3587                        | Value::Range { .. }
3588                        | Value::String { .. }
3589                        | Value::Record { .. }
3590                        | Value::Custom { .. }
3591                ) {
3592                    ShellError::OperatorIncompatibleTypes {
3593                        op: Operator::Comparison(Comparison::In),
3594                        lhs: lhs.get_type(),
3595                        rhs: rhs.get_type(),
3596                        op_span: op,
3597                        lhs_span: lhs.span(),
3598                        rhs_span: rhs.span(),
3599                        help: None,
3600                    }
3601                } else {
3602                    ShellError::OperatorUnsupportedType {
3603                        op: Operator::Comparison(Comparison::In),
3604                        unsupported: rhs.get_type(),
3605                        op_span: op,
3606                        unsupported_span: rhs.span(),
3607                        help: None,
3608                    }
3609                },
3610            ),
3611        }
3612    }
3613
3614    pub fn not_in(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3615        match (self, rhs) {
3616            (lhs, Value::Range { val: rhs, .. }) => Ok(Value::bool(!rhs.contains(lhs), span)),
3617            (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
3618                Ok(Value::bool(!rhs.contains(lhs), span))
3619            }
3620            (lhs, Value::List { vals: rhs, .. }) => Ok(Value::bool(!rhs.contains(lhs), span)),
3621            (Value::String { val: lhs, .. }, Value::Record { val: rhs, .. }) => {
3622                Ok(Value::bool(!rhs.contains(lhs), span))
3623            }
3624            (Value::String { .. } | Value::Int { .. }, Value::CellPath { val: rhs, .. }) => {
3625                let val = rhs.members.iter().any(|member| match (self, member) {
3626                    (Value::Int { val: lhs, .. }, PathMember::Int { val: rhs, .. }) => {
3627                        *lhs != *rhs as i64
3628                    }
3629                    (Value::String { val: lhs, .. }, PathMember::String { val: rhs, .. }) => {
3630                        lhs != rhs
3631                    }
3632                    (Value::String { .. }, PathMember::Int { .. })
3633                    | (Value::Int { .. }, PathMember::String { .. }) => true,
3634                    _ => unreachable!(
3635                        "outer match arm ensures `self` is either a `String` or `Int` variant"
3636                    ),
3637                });
3638
3639                Ok(Value::bool(val, span))
3640            }
3641            (Value::CellPath { val: lhs, .. }, Value::CellPath { val: rhs, .. }) => {
3642                Ok(Value::bool(
3643                    rhs.members
3644                        .windows(lhs.members.len())
3645                        .all(|member_window| member_window != rhs.members),
3646                    span,
3647                ))
3648            }
3649            (Value::Custom { val: lhs, .. }, rhs) => lhs.operation(
3650                self.span(),
3651                Operator::Comparison(Comparison::NotIn),
3652                op,
3653                rhs,
3654            ),
3655            (lhs, rhs) => Err(
3656                if matches!(
3657                    rhs,
3658                    Value::List { .. }
3659                        | Value::Range { .. }
3660                        | Value::String { .. }
3661                        | Value::Record { .. }
3662                        | Value::Custom { .. }
3663                ) {
3664                    ShellError::OperatorIncompatibleTypes {
3665                        op: Operator::Comparison(Comparison::NotIn),
3666                        lhs: lhs.get_type(),
3667                        rhs: rhs.get_type(),
3668                        op_span: op,
3669                        lhs_span: lhs.span(),
3670                        rhs_span: rhs.span(),
3671                        help: None,
3672                    }
3673                } else {
3674                    ShellError::OperatorUnsupportedType {
3675                        op: Operator::Comparison(Comparison::NotIn),
3676                        unsupported: rhs.get_type(),
3677                        op_span: op,
3678                        unsupported_span: rhs.span(),
3679                        help: None,
3680                    }
3681                },
3682            ),
3683        }
3684    }
3685
3686    pub fn has(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3687        rhs.r#in(op, self, span)
3688    }
3689
3690    pub fn not_has(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3691        rhs.r#not_in(op, self, span)
3692    }
3693
3694    pub fn regex_match(
3695        &self,
3696        engine_state: &EngineState,
3697        op: Span,
3698        rhs: &Value,
3699        invert: bool,
3700        span: Span,
3701    ) -> Result<Value, ShellError> {
3702        let rhs_span = rhs.span();
3703        match (self, rhs) {
3704            (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
3705                let is_match = match engine_state.regex_cache.try_lock() {
3706                    Ok(mut cache) => {
3707                        if let Some(regex) = cache.get(rhs) {
3708                            regex.is_match(lhs)
3709                        } else {
3710                            let regex =
3711                                Regex::new(rhs).map_err(|e| ShellError::UnsupportedInput {
3712                                    msg: format!("{e}"),
3713                                    input: "value originated from here".into(),
3714                                    msg_span: span,
3715                                    input_span: rhs_span,
3716                                })?;
3717                            let ret = regex.is_match(lhs);
3718                            cache.put(rhs.clone(), regex);
3719                            ret
3720                        }
3721                    }
3722                    Err(_) => {
3723                        let regex = Regex::new(rhs).map_err(|e| ShellError::UnsupportedInput {
3724                            msg: format!("{e}"),
3725                            input: "value originated from here".into(),
3726                            msg_span: span,
3727                            input_span: rhs_span,
3728                        })?;
3729                        regex.is_match(lhs)
3730                    }
3731                };
3732
3733                Ok(Value::bool(
3734                    if invert {
3735                        !is_match.unwrap_or(false)
3736                    } else {
3737                        is_match.unwrap_or(true)
3738                    },
3739                    span,
3740                ))
3741            }
3742            (Value::Custom { val: lhs, .. }, rhs) => lhs.operation(
3743                span,
3744                if invert {
3745                    Operator::Comparison(Comparison::NotRegexMatch)
3746                } else {
3747                    Operator::Comparison(Comparison::RegexMatch)
3748                },
3749                op,
3750                rhs,
3751            ),
3752            _ => Err(operator_type_error(
3753                if invert {
3754                    Operator::Comparison(Comparison::NotRegexMatch)
3755                } else {
3756                    Operator::Comparison(Comparison::RegexMatch)
3757                },
3758                op,
3759                self,
3760                rhs,
3761                |val| matches!(val, Value::String { .. }),
3762            )),
3763        }
3764    }
3765
3766    pub fn starts_with(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3767        match (self, rhs) {
3768            (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
3769                Ok(Value::bool(lhs.starts_with(rhs), span))
3770            }
3771            (Value::Custom { val: lhs, .. }, rhs) => lhs.operation(
3772                self.span(),
3773                Operator::Comparison(Comparison::StartsWith),
3774                op,
3775                rhs,
3776            ),
3777            _ => Err(operator_type_error(
3778                Operator::Comparison(Comparison::StartsWith),
3779                op,
3780                self,
3781                rhs,
3782                |val| matches!(val, Value::String { .. }),
3783            )),
3784        }
3785    }
3786
3787    pub fn ends_with(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3788        match (self, rhs) {
3789            (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
3790                Ok(Value::bool(lhs.ends_with(rhs), span))
3791            }
3792            (Value::Custom { val: lhs, .. }, rhs) => lhs.operation(
3793                self.span(),
3794                Operator::Comparison(Comparison::EndsWith),
3795                op,
3796                rhs,
3797            ),
3798            _ => Err(operator_type_error(
3799                Operator::Comparison(Comparison::EndsWith),
3800                op,
3801                self,
3802                rhs,
3803                |val| matches!(val, Value::String { .. }),
3804            )),
3805        }
3806    }
3807
3808    pub fn bit_or(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3809        match (self, rhs) {
3810            (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3811                Ok(Value::int(*lhs | rhs, span))
3812            }
3813            (Value::Custom { val: lhs, .. }, rhs) => {
3814                lhs.operation(span, Operator::Bits(Bits::BitOr), op, rhs)
3815            }
3816            _ => Err(operator_type_error(
3817                Operator::Bits(Bits::BitOr),
3818                op,
3819                self,
3820                rhs,
3821                |val| matches!(val, Value::Int { .. }),
3822            )),
3823        }
3824    }
3825
3826    pub fn bit_xor(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3827        match (self, rhs) {
3828            (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3829                Ok(Value::int(*lhs ^ rhs, span))
3830            }
3831            (Value::Custom { val: lhs, .. }, rhs) => {
3832                lhs.operation(span, Operator::Bits(Bits::BitXor), op, rhs)
3833            }
3834            _ => Err(operator_type_error(
3835                Operator::Bits(Bits::BitXor),
3836                op,
3837                self,
3838                rhs,
3839                |val| matches!(val, Value::Int { .. }),
3840            )),
3841        }
3842    }
3843
3844    pub fn bit_and(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3845        match (self, rhs) {
3846            (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3847                Ok(Value::int(*lhs & rhs, span))
3848            }
3849            (Value::Custom { val: lhs, .. }, rhs) => {
3850                lhs.operation(span, Operator::Bits(Bits::BitAnd), op, rhs)
3851            }
3852            _ => Err(operator_type_error(
3853                Operator::Bits(Bits::BitAnd),
3854                op,
3855                self,
3856                rhs,
3857                |val| matches!(val, Value::Int { .. }),
3858            )),
3859        }
3860    }
3861
3862    pub fn bit_shl(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3863        match (self, rhs) {
3864            (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3865                // Currently we disallow negative operands like Rust's `Shl`
3866                // Cheap guarding with TryInto<u32>
3867                if let Some(val) = (*rhs).try_into().ok().and_then(|rhs| lhs.checked_shl(rhs)) {
3868                    Ok(Value::int(val, span))
3869                } else {
3870                    Err(ShellError::OperatorOverflow {
3871                        msg: "right operand to bit-shl exceeds available bits in underlying data"
3872                            .into(),
3873                        span,
3874                        help: Some(format!("Limit operand to 0 <= rhs < {}", i64::BITS)),
3875                    })
3876                }
3877            }
3878            (Value::Custom { val: lhs, .. }, rhs) => {
3879                lhs.operation(span, Operator::Bits(Bits::ShiftLeft), op, rhs)
3880            }
3881            _ => Err(operator_type_error(
3882                Operator::Bits(Bits::ShiftLeft),
3883                op,
3884                self,
3885                rhs,
3886                |val| matches!(val, Value::Int { .. }),
3887            )),
3888        }
3889    }
3890
3891    pub fn bit_shr(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3892        match (self, rhs) {
3893            (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3894                // Currently we disallow negative operands like Rust's `Shr`
3895                // Cheap guarding with TryInto<u32>
3896                if let Some(val) = (*rhs).try_into().ok().and_then(|rhs| lhs.checked_shr(rhs)) {
3897                    Ok(Value::int(val, span))
3898                } else {
3899                    Err(ShellError::OperatorOverflow {
3900                        msg: "right operand to bit-shr exceeds available bits in underlying data"
3901                            .into(),
3902                        span,
3903                        help: Some(format!("Limit operand to 0 <= rhs < {}", i64::BITS)),
3904                    })
3905                }
3906            }
3907            (Value::Custom { val: lhs, .. }, rhs) => {
3908                lhs.operation(span, Operator::Bits(Bits::ShiftRight), op, rhs)
3909            }
3910            _ => Err(operator_type_error(
3911                Operator::Bits(Bits::ShiftRight),
3912                op,
3913                self,
3914                rhs,
3915                |val| matches!(val, Value::Int { .. }),
3916            )),
3917        }
3918    }
3919
3920    pub fn or(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3921        match (self, rhs) {
3922            (Value::Bool { val: lhs, .. }, Value::Bool { val: rhs, .. }) => {
3923                Ok(Value::bool(*lhs || *rhs, span))
3924            }
3925            (Value::Custom { val: lhs, .. }, rhs) => {
3926                lhs.operation(span, Operator::Boolean(Boolean::Or), op, rhs)
3927            }
3928            _ => Err(operator_type_error(
3929                Operator::Boolean(Boolean::Or),
3930                op,
3931                self,
3932                rhs,
3933                |val| matches!(val, Value::Bool { .. }),
3934            )),
3935        }
3936    }
3937
3938    pub fn xor(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3939        match (self, rhs) {
3940            (Value::Bool { val: lhs, .. }, Value::Bool { val: rhs, .. }) => {
3941                Ok(Value::bool((*lhs && !*rhs) || (!*lhs && *rhs), span))
3942            }
3943            (Value::Custom { val: lhs, .. }, rhs) => {
3944                lhs.operation(span, Operator::Boolean(Boolean::Xor), op, rhs)
3945            }
3946            _ => Err(operator_type_error(
3947                Operator::Boolean(Boolean::Xor),
3948                op,
3949                self,
3950                rhs,
3951                |val| matches!(val, Value::Bool { .. }),
3952            )),
3953        }
3954    }
3955
3956    pub fn and(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3957        match (self, rhs) {
3958            (Value::Bool { val: lhs, .. }, Value::Bool { val: rhs, .. }) => {
3959                Ok(Value::bool(*lhs && *rhs, span))
3960            }
3961            (Value::Custom { val: lhs, .. }, rhs) => {
3962                lhs.operation(span, Operator::Boolean(Boolean::And), op, rhs)
3963            }
3964            _ => Err(operator_type_error(
3965                Operator::Boolean(Boolean::And),
3966                op,
3967                self,
3968                rhs,
3969                |val| matches!(val, Value::Bool { .. }),
3970            )),
3971        }
3972    }
3973}
3974
3975// TODO: The name of this function is overly broad with partial compatibility
3976// Should be replaced by an explicitly named helper on `Type` (take `Any` into account)
3977fn type_compatible(a: Type, b: Type) -> bool {
3978    if a == b {
3979        return true;
3980    }
3981
3982    matches!((a, b), (Type::Int, Type::Float) | (Type::Float, Type::Int))
3983}
3984
3985fn operator_type_error(
3986    op: Operator,
3987    op_span: Span,
3988    lhs: &Value,
3989    rhs: &Value,
3990    is_supported: fn(&Value) -> bool,
3991) -> ShellError {
3992    let is_supported = |val| is_supported(val) || matches!(val, Value::Custom { .. });
3993    match (is_supported(lhs), is_supported(rhs)) {
3994        (true, true) => ShellError::OperatorIncompatibleTypes {
3995            op,
3996            lhs: lhs.get_type(),
3997            rhs: rhs.get_type(),
3998            op_span,
3999            lhs_span: lhs.span(),
4000            rhs_span: rhs.span(),
4001            help: None,
4002        },
4003        (true, false) => ShellError::OperatorUnsupportedType {
4004            op,
4005            unsupported: rhs.get_type(),
4006            op_span,
4007            unsupported_span: rhs.span(),
4008            help: None,
4009        },
4010        (false, _) => ShellError::OperatorUnsupportedType {
4011            op,
4012            unsupported: lhs.get_type(),
4013            op_span,
4014            unsupported_span: lhs.span(),
4015            help: None,
4016        },
4017    }
4018}
4019
4020fn human_time_from_now(val: &DateTime<FixedOffset>) -> HumanTime {
4021    let now = Local::now().with_timezone(val.offset());
4022    let delta = *val - now;
4023    match delta.num_nanoseconds() {
4024        Some(num_nanoseconds) => {
4025            let delta_seconds = num_nanoseconds as f64 / 1_000_000_000.0;
4026            let delta_seconds_rounded = delta_seconds.round() as i64;
4027            HumanTime::from(Duration::seconds(delta_seconds_rounded))
4028        }
4029        None => {
4030            // Happens if the total number of nanoseconds exceeds what fits in an i64
4031            // Note: not using delta.num_days() because it results is wrong for years before ~936: a extra year is added
4032            let delta_years = val.year() - now.year();
4033            HumanTime::from(Duration::days(delta_years as i64 * 365))
4034        }
4035    }
4036}
4037
4038#[cfg(test)]
4039mod tests {
4040    use super::{Record, Value};
4041    use crate::record;
4042
4043    mod at_cell_path {
4044        use crate::{IntoValue, Span};
4045
4046        use super::super::PathMember;
4047        use super::*;
4048
4049        #[test]
4050        fn test_record_with_data_at_cell_path() {
4051            let value_to_insert = Value::test_string("value");
4052            let span = Span::test_data();
4053            assert_eq!(
4054                Value::with_data_at_cell_path(
4055                    &[
4056                        PathMember::test_string("a".to_string(), false),
4057                        PathMember::test_string("b".to_string(), false),
4058                        PathMember::test_string("c".to_string(), false),
4059                        PathMember::test_string("d".to_string(), false),
4060                    ],
4061                    value_to_insert,
4062                ),
4063                // {a:{b:c{d:"value"}}}
4064                Ok(record!(
4065                    "a" => record!(
4066                        "b" => record!(
4067                            "c" => record!(
4068                                "d" => Value::test_string("value")
4069                            ).into_value(span)
4070                        ).into_value(span)
4071                    ).into_value(span)
4072                )
4073                .into_value(span))
4074            );
4075        }
4076
4077        #[test]
4078        fn test_lists_with_data_at_cell_path() {
4079            let value_to_insert = Value::test_string("value");
4080            assert_eq!(
4081                Value::with_data_at_cell_path(
4082                    &[
4083                        PathMember::test_int(0, false),
4084                        PathMember::test_int(0, false),
4085                        PathMember::test_int(0, false),
4086                        PathMember::test_int(0, false),
4087                    ],
4088                    value_to_insert.clone(),
4089                ),
4090                // [[[[["value"]]]]]
4091                Ok(Value::test_list(vec![Value::test_list(vec![
4092                    Value::test_list(vec![Value::test_list(vec![value_to_insert])])
4093                ])]))
4094            );
4095        }
4096        #[test]
4097        fn test_mixed_with_data_at_cell_path() {
4098            let value_to_insert = Value::test_string("value");
4099            let span = Span::test_data();
4100            assert_eq!(
4101                Value::with_data_at_cell_path(
4102                    &[
4103                        PathMember::test_string("a".to_string(), false),
4104                        PathMember::test_int(0, false),
4105                        PathMember::test_string("b".to_string(), false),
4106                        PathMember::test_int(0, false),
4107                        PathMember::test_string("c".to_string(), false),
4108                        PathMember::test_int(0, false),
4109                        PathMember::test_string("d".to_string(), false),
4110                        PathMember::test_int(0, false),
4111                    ],
4112                    value_to_insert.clone(),
4113                ),
4114                // [{a:[{b:[{c:[{d:["value"]}]}]}]]}
4115                Ok(record!(
4116                    "a" => Value::test_list(vec![record!(
4117                        "b" => Value::test_list(vec![record!(
4118                            "c" => Value::test_list(vec![record!(
4119                                "d" => Value::test_list(vec![value_to_insert])
4120                            ).into_value(span)])
4121                        ).into_value(span)])
4122                    ).into_value(span)])
4123                )
4124                .into_value(span))
4125            );
4126        }
4127
4128        #[test]
4129        fn test_nested_upsert_data_at_cell_path() {
4130            let span = Span::test_data();
4131            let mut base_value = record!(
4132                "a" => Value::test_list(vec![])
4133            )
4134            .into_value(span);
4135
4136            let value_to_insert = Value::test_string("value");
4137            let res = base_value.upsert_data_at_cell_path(
4138                &[
4139                    PathMember::test_string("a".to_string(), false),
4140                    PathMember::test_int(0, false),
4141                    PathMember::test_string("b".to_string(), false),
4142                    PathMember::test_int(0, false),
4143                ],
4144                value_to_insert.clone(),
4145            );
4146            assert_eq!(res, Ok(()));
4147            assert_eq!(
4148                base_value,
4149                // {a:[{b:["value"]}]}
4150                record!(
4151                    "a" => Value::test_list(vec![
4152                        record!(
4153                            "b" => Value::test_list(vec![value_to_insert])
4154                        )
4155                        .into_value(span)
4156                    ])
4157                )
4158                .into_value(span)
4159            );
4160        }
4161
4162        #[test]
4163        fn test_nested_insert_data_at_cell_path() {
4164            let span = Span::test_data();
4165            let mut base_value = record!(
4166                "a" => Value::test_list(vec![])
4167            )
4168            .into_value(span);
4169
4170            let value_to_insert = Value::test_string("value");
4171            let res = base_value.insert_data_at_cell_path(
4172                &[
4173                    PathMember::test_string("a".to_string(), false),
4174                    PathMember::test_int(0, false),
4175                    PathMember::test_string("b".to_string(), false),
4176                    PathMember::test_int(0, false),
4177                ],
4178                value_to_insert.clone(),
4179                span,
4180            );
4181            assert_eq!(res, Ok(()));
4182            assert_eq!(
4183                base_value,
4184                // {a:[{b:["value"]}]}
4185                record!(
4186                    "a" => Value::test_list(vec![
4187                        record!(
4188                            "b" => Value::test_list(vec![value_to_insert])
4189                        )
4190                        .into_value(span)
4191                    ])
4192                )
4193                .into_value(span)
4194            );
4195        }
4196    }
4197
4198    mod is_empty {
4199        use super::*;
4200
4201        #[test]
4202        fn test_string() {
4203            let value = Value::test_string("");
4204            assert!(value.is_empty());
4205        }
4206
4207        #[test]
4208        fn test_list() {
4209            let list_with_no_values = Value::test_list(vec![]);
4210            let list_with_one_empty_string = Value::test_list(vec![Value::test_string("")]);
4211
4212            assert!(list_with_no_values.is_empty());
4213            assert!(!list_with_one_empty_string.is_empty());
4214        }
4215
4216        #[test]
4217        fn test_record() {
4218            let no_columns_nor_cell_values = Value::test_record(Record::new());
4219
4220            let one_column_and_one_cell_value_with_empty_strings = Value::test_record(record! {
4221                "" => Value::test_string(""),
4222            });
4223
4224            let one_column_with_a_string_and_one_cell_value_with_empty_string =
4225                Value::test_record(record! {
4226                    "column" => Value::test_string(""),
4227                });
4228
4229            let one_column_with_empty_string_and_one_value_with_a_string =
4230                Value::test_record(record! {
4231                    "" => Value::test_string("text"),
4232                });
4233
4234            assert!(no_columns_nor_cell_values.is_empty());
4235            assert!(!one_column_and_one_cell_value_with_empty_strings.is_empty());
4236            assert!(!one_column_with_a_string_and_one_cell_value_with_empty_string.is_empty());
4237            assert!(!one_column_with_empty_string_and_one_value_with_a_string.is_empty());
4238        }
4239    }
4240
4241    mod get_type {
4242        use crate::Type;
4243
4244        use super::*;
4245
4246        #[test]
4247        fn test_list() {
4248            let list_of_ints = Value::test_list(vec![Value::test_int(0)]);
4249            let list_of_floats = Value::test_list(vec![Value::test_float(0.0)]);
4250            let list_of_ints_and_floats =
4251                Value::test_list(vec![Value::test_int(0), Value::test_float(0.0)]);
4252            let list_of_ints_and_floats_and_bools = Value::test_list(vec![
4253                Value::test_int(0),
4254                Value::test_float(0.0),
4255                Value::test_bool(false),
4256            ]);
4257            assert_eq!(list_of_ints.get_type(), Type::List(Box::new(Type::Int)));
4258            assert_eq!(list_of_floats.get_type(), Type::List(Box::new(Type::Float)));
4259            assert_eq!(
4260                list_of_ints_and_floats_and_bools.get_type(),
4261                Type::List(Box::new(Type::Any))
4262            );
4263            assert_eq!(
4264                list_of_ints_and_floats.get_type(),
4265                Type::List(Box::new(Type::Number))
4266            );
4267        }
4268    }
4269
4270    mod is_subtype {
4271        use crate::Type;
4272
4273        use super::*;
4274
4275        fn assert_subtype_equivalent(value: &Value, ty: &Type) {
4276            assert_eq!(value.is_subtype_of(ty), value.get_type().is_subtype_of(ty));
4277        }
4278
4279        #[test]
4280        fn test_list() {
4281            let ty_int_list = Type::list(Type::Int);
4282            let ty_str_list = Type::list(Type::String);
4283            let ty_any_list = Type::list(Type::Any);
4284            let ty_list_list_int = Type::list(Type::list(Type::Int));
4285
4286            let list = Value::test_list(vec![
4287                Value::test_int(1),
4288                Value::test_int(2),
4289                Value::test_int(3),
4290            ]);
4291
4292            assert_subtype_equivalent(&list, &ty_int_list);
4293            assert_subtype_equivalent(&list, &ty_str_list);
4294            assert_subtype_equivalent(&list, &ty_any_list);
4295
4296            let list = Value::test_list(vec![
4297                Value::test_int(1),
4298                Value::test_string("hi"),
4299                Value::test_int(3),
4300            ]);
4301
4302            assert_subtype_equivalent(&list, &ty_int_list);
4303            assert_subtype_equivalent(&list, &ty_str_list);
4304            assert_subtype_equivalent(&list, &ty_any_list);
4305
4306            let list = Value::test_list(vec![Value::test_list(vec![Value::test_int(1)])]);
4307
4308            assert_subtype_equivalent(&list, &ty_list_list_int);
4309
4310            // The type of an empty lists is a subtype of any list or table type
4311            let ty_table = Type::Table(Box::new([
4312                ("a".into(), Type::Int),
4313                ("b".into(), Type::Int),
4314                ("c".into(), Type::Int),
4315            ]));
4316            let empty = Value::test_list(vec![]);
4317
4318            assert_subtype_equivalent(&empty, &ty_any_list);
4319            assert!(empty.is_subtype_of(&ty_int_list));
4320            assert!(empty.is_subtype_of(&ty_table));
4321        }
4322
4323        #[test]
4324        fn test_record() {
4325            let ty_abc = Type::Record(Box::new([
4326                ("a".into(), Type::Int),
4327                ("b".into(), Type::Int),
4328                ("c".into(), Type::Int),
4329            ]));
4330            let ty_ab = Type::Record(Box::new([("a".into(), Type::Int), ("b".into(), Type::Int)]));
4331            let ty_inner = Type::Record(Box::new([("inner".into(), ty_abc.clone())]));
4332
4333            let record_abc = Value::test_record(record! {
4334                "a" => Value::test_int(1),
4335                "b" => Value::test_int(2),
4336                "c" => Value::test_int(3),
4337            });
4338            let record_ab = Value::test_record(record! {
4339                "a" => Value::test_int(1),
4340                "b" => Value::test_int(2),
4341            });
4342
4343            assert_subtype_equivalent(&record_abc, &ty_abc);
4344            assert_subtype_equivalent(&record_abc, &ty_ab);
4345            assert_subtype_equivalent(&record_ab, &ty_abc);
4346            assert_subtype_equivalent(&record_ab, &ty_ab);
4347
4348            let record_inner = Value::test_record(record! {
4349                "inner" => record_abc
4350            });
4351            assert_subtype_equivalent(&record_inner, &ty_inner);
4352        }
4353
4354        #[test]
4355        fn test_table() {
4356            let ty_abc = Type::Table(Box::new([
4357                ("a".into(), Type::Int),
4358                ("b".into(), Type::Int),
4359                ("c".into(), Type::Int),
4360            ]));
4361            let ty_ab = Type::Table(Box::new([("a".into(), Type::Int), ("b".into(), Type::Int)]));
4362            let ty_list_any = Type::list(Type::Any);
4363
4364            let record_abc = Value::test_record(record! {
4365                "a" => Value::test_int(1),
4366                "b" => Value::test_int(2),
4367                "c" => Value::test_int(3),
4368            });
4369            let record_ab = Value::test_record(record! {
4370                "a" => Value::test_int(1),
4371                "b" => Value::test_int(2),
4372            });
4373
4374            let table_abc = Value::test_list(vec![record_abc.clone(), record_abc.clone()]);
4375            let table_ab = Value::test_list(vec![record_ab.clone(), record_ab.clone()]);
4376
4377            assert_subtype_equivalent(&table_abc, &ty_abc);
4378            assert_subtype_equivalent(&table_abc, &ty_ab);
4379            assert_subtype_equivalent(&table_ab, &ty_abc);
4380            assert_subtype_equivalent(&table_ab, &ty_ab);
4381            assert_subtype_equivalent(&table_abc, &ty_list_any);
4382
4383            let table_mixed = Value::test_list(vec![record_abc.clone(), record_ab.clone()]);
4384            assert_subtype_equivalent(&table_mixed, &ty_abc);
4385            assert!(table_mixed.is_subtype_of(&ty_ab));
4386
4387            let ty_a = Type::Table(Box::new([("a".into(), Type::Any)]));
4388            let table_mixed_types = Value::test_list(vec![
4389                Value::test_record(record! {
4390                    "a" => Value::test_int(1),
4391                }),
4392                Value::test_record(record! {
4393                    "a" => Value::test_string("a"),
4394                }),
4395            ]);
4396            assert!(table_mixed_types.is_subtype_of(&ty_a));
4397        }
4398    }
4399
4400    mod into_string {
4401        use chrono::{DateTime, FixedOffset};
4402
4403        use super::*;
4404
4405        #[test]
4406        fn test_datetime() {
4407            let date = DateTime::from_timestamp_millis(-123456789)
4408                .unwrap()
4409                .with_timezone(&FixedOffset::east_opt(0).unwrap());
4410
4411            let string = Value::test_date(date).to_expanded_string("", &Default::default());
4412
4413            // We need to cut the humanized part off for tests to work, because
4414            // it is relative to current time.
4415            let formatted = string.split('(').next().unwrap();
4416            assert_eq!("Tue, 30 Dec 1969 13:42:23 +0000 ", formatted);
4417        }
4418
4419        #[test]
4420        fn test_negative_year_datetime() {
4421            let date = DateTime::from_timestamp_millis(-72135596800000)
4422                .unwrap()
4423                .with_timezone(&FixedOffset::east_opt(0).unwrap());
4424
4425            let string = Value::test_date(date).to_expanded_string("", &Default::default());
4426
4427            // We need to cut the humanized part off for tests to work, because
4428            // it is relative to current time.
4429            let formatted = string.split(' ').next().unwrap();
4430            assert_eq!("-0316-02-11T06:13:20+00:00", formatted);
4431        }
4432    }
4433
4434    #[test]
4435    fn test_env_as_bool() {
4436        // explicit false values
4437        assert_eq!(Value::test_bool(false).coerce_bool(), Ok(false));
4438        assert_eq!(Value::test_int(0).coerce_bool(), Ok(false));
4439        assert_eq!(Value::test_float(0.0).coerce_bool(), Ok(false));
4440        assert_eq!(Value::test_string("").coerce_bool(), Ok(false));
4441        assert_eq!(Value::test_string("0").coerce_bool(), Ok(false));
4442        assert_eq!(Value::test_nothing().coerce_bool(), Ok(false));
4443
4444        // explicit true values
4445        assert_eq!(Value::test_bool(true).coerce_bool(), Ok(true));
4446        assert_eq!(Value::test_int(1).coerce_bool(), Ok(true));
4447        assert_eq!(Value::test_float(1.0).coerce_bool(), Ok(true));
4448        assert_eq!(Value::test_string("1").coerce_bool(), Ok(true));
4449
4450        // implicit true values
4451        assert_eq!(Value::test_int(42).coerce_bool(), Ok(true));
4452        assert_eq!(Value::test_float(0.5).coerce_bool(), Ok(true));
4453        assert_eq!(Value::test_string("not zero").coerce_bool(), Ok(true));
4454
4455        // complex values returning None
4456        assert!(Value::test_record(Record::default()).coerce_bool().is_err());
4457        assert!(Value::test_list(vec![Value::test_int(1)])
4458            .coerce_bool()
4459            .is_err());
4460        assert!(Value::test_date(
4461            chrono::DateTime::parse_from_rfc3339("2024-01-01T12:00:00+00:00").unwrap(),
4462        )
4463        .coerce_bool()
4464        .is_err());
4465        assert!(Value::test_glob("*.rs").coerce_bool().is_err());
4466        assert!(Value::test_binary(vec![1, 2, 3]).coerce_bool().is_err());
4467        assert!(Value::test_duration(3600).coerce_bool().is_err());
4468    }
4469}