nu_protocol/value/
mod.rs

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