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