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