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