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