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