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