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