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