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