nu_protocol/value/
mod.rs

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