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