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