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