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