Skip to main content

nu_protocol/value/
mod.rs

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