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