Skip to main content

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