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