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 && val.year() <= 9999 {
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 try_put_int_path_member_on_top(cell_paths: &[PathMember]) -> Option<Vec<PathMember>> {
1103 if !nu_experimental::REORDER_CELL_PATHS.get() {
1104 return None;
1105 }
1106 let idx = cell_paths
1107 .iter()
1108 .position(|pm| matches!(pm, PathMember::Int { .. }));
1109 idx.map(|idx| {
1110 let mut cell_paths = cell_paths.to_vec();
1111 cell_paths[0..idx + 1].rotate_right(1);
1112 cell_paths
1113 })
1114 }
1115
1116 pub fn follow_cell_path<'out>(
1118 &'out self,
1119 cell_path: &[PathMember],
1120 ) -> Result<Cow<'out, Value>, ShellError> {
1121 let mut store: Value = Value::test_nothing();
1124 let mut current: MultiLife<'out, '_, Value> = MultiLife::Out(self);
1125
1126 let reorder_cell_paths = nu_experimental::REORDER_CELL_PATHS.get();
1127
1128 let mut members: Vec<_> = if reorder_cell_paths {
1129 cell_path.iter().map(Some).collect()
1130 } else {
1131 Vec::new()
1132 };
1133 let mut members = members.as_mut_slice();
1134 let mut cell_path = cell_path;
1135
1136 loop {
1137 let member = if reorder_cell_paths {
1138 while let Some(None) = members.first() {
1140 members = &mut members[1..];
1141 }
1142
1143 if members.is_empty() {
1144 break;
1145 }
1146
1147 let member = if let Value::List { .. } = &*current {
1150 members
1152 .iter_mut()
1153 .find(|x| matches!(x, Some(PathMember::Int { .. })))
1154 .and_then(Option::take)
1156 } else {
1157 None
1158 };
1159
1160 let Some(member) = member.or_else(|| members.first_mut().and_then(Option::take))
1161 else {
1162 break;
1163 };
1164 member
1165 } else {
1166 match cell_path {
1167 [first, rest @ ..] => {
1168 cell_path = rest;
1169 first
1170 }
1171 _ => break,
1172 }
1173 };
1174
1175 current = match current {
1176 MultiLife::Out(current) => match get_value_member(current, member)? {
1177 ControlFlow::Break(span) => return Ok(Cow::Owned(Value::nothing(span))),
1178 ControlFlow::Continue(x) => match x {
1179 Cow::Borrowed(x) => MultiLife::Out(x),
1180 Cow::Owned(x) => {
1181 store = x;
1182 MultiLife::Local(&store)
1183 }
1184 },
1185 },
1186 MultiLife::Local(current) => match get_value_member(current, member)? {
1187 ControlFlow::Break(span) => return Ok(Cow::Owned(Value::nothing(span))),
1188 ControlFlow::Continue(x) => match x {
1189 Cow::Borrowed(x) => MultiLife::Local(x),
1190 Cow::Owned(x) => {
1191 store = x;
1192 MultiLife::Local(&store)
1193 }
1194 },
1195 },
1196 };
1197 }
1198
1199 if let Value::Error { error, .. } = &*current {
1202 Err(error.as_ref().clone())
1203 } else {
1204 Ok(match current {
1205 MultiLife::Out(x) => Cow::Borrowed(x),
1206 MultiLife::Local(x) => {
1207 let x = if std::ptr::eq(x, &store) {
1208 store
1209 } else {
1210 x.clone()
1211 };
1212 Cow::Owned(x)
1213 }
1214 })
1215 }
1216 }
1217
1218 pub fn upsert_cell_path(
1220 &mut self,
1221 cell_path: &[PathMember],
1222 callback: Box<dyn FnOnce(&Value) -> Value>,
1223 ) -> Result<(), ShellError> {
1224 let new_val = callback(self.follow_cell_path(cell_path)?.as_ref());
1225
1226 match new_val {
1227 Value::Error { error, .. } => Err(*error),
1228 new_val => self.upsert_data_at_cell_path(cell_path, new_val),
1229 }
1230 }
1231
1232 pub fn upsert_data_at_cell_path(
1233 &mut self,
1234 cell_path: &[PathMember],
1235 new_val: Value,
1236 ) -> Result<(), ShellError> {
1237 let v_span = self.span();
1238 if let Some((member, path)) = cell_path.split_first() {
1239 match member {
1240 PathMember::String {
1241 val: col_name,
1242 span,
1243 casing,
1244 ..
1245 } => match self {
1246 Value::List { vals, .. } => {
1247 if let Some(new_cell_path) = Self::try_put_int_path_member_on_top(cell_path)
1248 {
1249 self.upsert_data_at_cell_path(&new_cell_path, new_val.clone())?;
1250 } else {
1251 for val in vals.iter_mut() {
1252 match val {
1253 Value::Record { val: record, .. } => {
1254 let record = record.to_mut();
1255 if let Some(val) =
1256 record.cased_mut(*casing).get_mut(col_name)
1257 {
1258 val.upsert_data_at_cell_path(path, new_val.clone())?;
1259 } else {
1260 let new_col = Value::with_data_at_cell_path(
1261 path,
1262 new_val.clone(),
1263 )?;
1264 record.push(col_name, new_col);
1265 }
1266 }
1267 Value::Error { error, .. } => return Err(*error.clone()),
1268 v => {
1269 return Err(ShellError::CantFindColumn {
1270 col_name: col_name.clone(),
1271 span: Some(*span),
1272 src_span: v.span(),
1273 });
1274 }
1275 }
1276 }
1277 }
1278 }
1279 Value::Record { val: record, .. } => {
1280 let record = record.to_mut();
1281 if let Some(val) = record.cased_mut(*casing).get_mut(col_name) {
1282 val.upsert_data_at_cell_path(path, new_val)?;
1283 } else {
1284 let new_col = Value::with_data_at_cell_path(path, new_val.clone())?;
1285 record.push(col_name, new_col);
1286 }
1287 }
1288 Value::Error { error, .. } => return Err(*error.clone()),
1289 v => {
1290 return Err(ShellError::CantFindColumn {
1291 col_name: col_name.clone(),
1292 span: Some(*span),
1293 src_span: v.span(),
1294 });
1295 }
1296 },
1297 PathMember::Int {
1298 val: row_num, span, ..
1299 } => match self {
1300 Value::List { vals, .. } => {
1301 if let Some(v) = vals.get_mut(*row_num) {
1302 v.upsert_data_at_cell_path(path, new_val)?;
1303 } else if vals.len() != *row_num {
1304 return Err(ShellError::InsertAfterNextFreeIndex {
1305 available_idx: vals.len(),
1306 span: *span,
1307 });
1308 } else {
1309 vals.push(Value::with_data_at_cell_path(path, new_val)?);
1311 }
1312 }
1313 Value::Error { error, .. } => return Err(*error.clone()),
1314 _ => {
1315 return Err(ShellError::NotAList {
1316 dst_span: *span,
1317 src_span: v_span,
1318 });
1319 }
1320 },
1321 }
1322 } else {
1323 *self = new_val;
1324 }
1325 Ok(())
1326 }
1327
1328 pub fn update_cell_path<'a>(
1330 &mut self,
1331 cell_path: &[PathMember],
1332 callback: Box<dyn FnOnce(&Value) -> Value + 'a>,
1333 ) -> Result<(), ShellError> {
1334 let new_val = callback(self.follow_cell_path(cell_path)?.as_ref());
1335
1336 match new_val {
1337 Value::Error { error, .. } => Err(*error),
1338 new_val => self.update_data_at_cell_path(cell_path, new_val),
1339 }
1340 }
1341
1342 pub fn update_data_at_cell_path(
1343 &mut self,
1344 cell_path: &[PathMember],
1345 new_val: Value,
1346 ) -> Result<(), ShellError> {
1347 let v_span = self.span();
1348 if let Some((member, path)) = cell_path.split_first() {
1349 match member {
1350 PathMember::String {
1351 val: col_name,
1352 span,
1353 casing,
1354 optional,
1355 } => match self {
1356 Value::List { vals, .. } => {
1357 if let Some(new_cell_path) = Self::try_put_int_path_member_on_top(cell_path)
1358 {
1359 self.upsert_data_at_cell_path(&new_cell_path, new_val.clone())?;
1360 } else {
1361 for val in vals.iter_mut() {
1362 let v_span = val.span();
1363 match val {
1364 Value::Record { val: record, .. } => {
1365 if let Some(val) =
1366 record.to_mut().cased_mut(*casing).get_mut(col_name)
1367 {
1368 val.update_data_at_cell_path(path, new_val.clone())?;
1369 } else if !*optional {
1370 return Err(ShellError::CantFindColumn {
1371 col_name: col_name.clone(),
1372 span: Some(*span),
1373 src_span: v_span,
1374 });
1375 }
1376 }
1377 Value::Error { error, .. } => return Err(*error.clone()),
1378 v => {
1379 if !*optional {
1380 return Err(ShellError::CantFindColumn {
1381 col_name: col_name.clone(),
1382 span: Some(*span),
1383 src_span: v.span(),
1384 });
1385 }
1386 }
1387 }
1388 }
1389 }
1390 }
1391 Value::Record { val: record, .. } => {
1392 if let Some(val) = record.to_mut().cased_mut(*casing).get_mut(col_name) {
1393 val.update_data_at_cell_path(path, new_val)?;
1394 } else if !*optional {
1395 return Err(ShellError::CantFindColumn {
1396 col_name: col_name.clone(),
1397 span: Some(*span),
1398 src_span: v_span,
1399 });
1400 }
1401 }
1402 Value::Error { error, .. } => return Err(*error.clone()),
1403 v => {
1404 if !*optional {
1405 return Err(ShellError::CantFindColumn {
1406 col_name: col_name.clone(),
1407 span: Some(*span),
1408 src_span: v.span(),
1409 });
1410 }
1411 }
1412 },
1413 PathMember::Int {
1414 val: row_num,
1415 span,
1416 optional,
1417 } => match self {
1418 Value::List { vals, .. } => {
1419 if let Some(v) = vals.get_mut(*row_num) {
1420 v.update_data_at_cell_path(path, new_val)?;
1421 } else if !*optional {
1422 if vals.is_empty() {
1423 return Err(ShellError::AccessEmptyContent { span: *span });
1424 } else {
1425 return Err(ShellError::AccessBeyondEnd {
1426 max_idx: vals.len() - 1,
1427 span: *span,
1428 });
1429 }
1430 }
1431 }
1432 Value::Error { error, .. } => return Err(*error.clone()),
1433 v => {
1434 return Err(ShellError::NotAList {
1435 dst_span: *span,
1436 src_span: v.span(),
1437 });
1438 }
1439 },
1440 }
1441 } else {
1442 *self = new_val;
1443 }
1444 Ok(())
1445 }
1446
1447 pub fn remove_data_at_cell_path(&mut self, cell_path: &[PathMember]) -> Result<(), ShellError> {
1448 match cell_path {
1449 [] => Ok(()),
1450 [member] => {
1451 let v_span = self.span();
1452 match member {
1453 PathMember::String {
1454 val: col_name,
1455 span,
1456 optional,
1457 casing,
1458 } => match self {
1459 Value::List { vals, .. } => {
1460 for val in vals.iter_mut() {
1461 let v_span = val.span();
1462 match val {
1463 Value::Record { val: record, .. } => {
1464 let value =
1465 record.to_mut().cased_mut(*casing).remove(col_name);
1466 if value.is_none() && !optional {
1467 return Err(ShellError::CantFindColumn {
1468 col_name: col_name.clone(),
1469 span: Some(*span),
1470 src_span: v_span,
1471 });
1472 }
1473 }
1474 v => {
1475 return Err(ShellError::CantFindColumn {
1476 col_name: col_name.clone(),
1477 span: Some(*span),
1478 src_span: v.span(),
1479 });
1480 }
1481 }
1482 }
1483 Ok(())
1484 }
1485 Value::Record { val: record, .. } => {
1486 if record
1487 .to_mut()
1488 .cased_mut(*casing)
1489 .remove(col_name)
1490 .is_none()
1491 && !optional
1492 {
1493 return Err(ShellError::CantFindColumn {
1494 col_name: col_name.clone(),
1495 span: Some(*span),
1496 src_span: v_span,
1497 });
1498 }
1499 Ok(())
1500 }
1501 v => Err(ShellError::CantFindColumn {
1502 col_name: col_name.clone(),
1503 span: Some(*span),
1504 src_span: v.span(),
1505 }),
1506 },
1507 PathMember::Int {
1508 val: row_num,
1509 span,
1510 optional,
1511 } => match self {
1512 Value::List { vals, .. } => {
1513 if vals.get_mut(*row_num).is_some() {
1514 vals.remove(*row_num);
1515 Ok(())
1516 } else if *optional {
1517 Ok(())
1518 } else if vals.is_empty() {
1519 Err(ShellError::AccessEmptyContent { span: *span })
1520 } else {
1521 Err(ShellError::AccessBeyondEnd {
1522 max_idx: vals.len() - 1,
1523 span: *span,
1524 })
1525 }
1526 }
1527 v => Err(ShellError::NotAList {
1528 dst_span: *span,
1529 src_span: v.span(),
1530 }),
1531 },
1532 }
1533 }
1534 [member, path @ ..] => {
1535 let v_span = self.span();
1536 match member {
1537 PathMember::String {
1538 val: col_name,
1539 span,
1540 optional,
1541 casing,
1542 } => match self {
1543 Value::List { vals, .. } => {
1544 for val in vals.iter_mut() {
1545 let v_span = val.span();
1546 match val {
1547 Value::Record { val: record, .. } => {
1548 let val =
1549 record.to_mut().cased_mut(*casing).get_mut(col_name);
1550 if let Some(val) = val {
1551 val.remove_data_at_cell_path(path)?;
1552 } else if !optional {
1553 return Err(ShellError::CantFindColumn {
1554 col_name: col_name.clone(),
1555 span: Some(*span),
1556 src_span: v_span,
1557 });
1558 }
1559 }
1560 v => {
1561 return Err(ShellError::CantFindColumn {
1562 col_name: col_name.clone(),
1563 span: Some(*span),
1564 src_span: v.span(),
1565 });
1566 }
1567 }
1568 }
1569 Ok(())
1570 }
1571 Value::Record { val: record, .. } => {
1572 if let Some(val) = record.to_mut().cased_mut(*casing).get_mut(col_name)
1573 {
1574 val.remove_data_at_cell_path(path)?;
1575 } else if !optional {
1576 return Err(ShellError::CantFindColumn {
1577 col_name: col_name.clone(),
1578 span: Some(*span),
1579 src_span: v_span,
1580 });
1581 }
1582 Ok(())
1583 }
1584 v => Err(ShellError::CantFindColumn {
1585 col_name: col_name.clone(),
1586 span: Some(*span),
1587 src_span: v.span(),
1588 }),
1589 },
1590 PathMember::Int {
1591 val: row_num,
1592 span,
1593 optional,
1594 } => match self {
1595 Value::List { vals, .. } => {
1596 if let Some(v) = vals.get_mut(*row_num) {
1597 v.remove_data_at_cell_path(path)
1598 } else if *optional {
1599 Ok(())
1600 } else if vals.is_empty() {
1601 Err(ShellError::AccessEmptyContent { span: *span })
1602 } else {
1603 Err(ShellError::AccessBeyondEnd {
1604 max_idx: vals.len() - 1,
1605 span: *span,
1606 })
1607 }
1608 }
1609 v => Err(ShellError::NotAList {
1610 dst_span: *span,
1611 src_span: v.span(),
1612 }),
1613 },
1614 }
1615 }
1616 }
1617 }
1618 pub fn insert_data_at_cell_path(
1619 &mut self,
1620 cell_path: &[PathMember],
1621 new_val: Value,
1622 head_span: Span,
1623 ) -> Result<(), ShellError> {
1624 let v_span = self.span();
1625 if let Some((member, path)) = cell_path.split_first() {
1626 match member {
1627 PathMember::String {
1628 val: col_name,
1629 span,
1630 casing,
1631 ..
1632 } => match self {
1633 Value::List { vals, .. } => {
1634 if let Some(new_cell_path) = Self::try_put_int_path_member_on_top(cell_path)
1635 {
1636 self.upsert_data_at_cell_path(&new_cell_path, new_val.clone())?;
1637 } else {
1638 for val in vals.iter_mut() {
1639 let v_span = val.span();
1640 match val {
1641 Value::Record { val: record, .. } => {
1642 let record = record.to_mut();
1643 if let Some(val) =
1644 record.cased_mut(*casing).get_mut(col_name)
1645 {
1646 if path.is_empty() {
1647 return Err(ShellError::ColumnAlreadyExists {
1648 col_name: col_name.clone(),
1649 span: *span,
1650 src_span: v_span,
1651 });
1652 } else {
1653 val.insert_data_at_cell_path(
1654 path,
1655 new_val.clone(),
1656 head_span,
1657 )?;
1658 }
1659 } else {
1660 let new_col = Value::with_data_at_cell_path(
1661 path,
1662 new_val.clone(),
1663 )?;
1664 record.push(col_name, new_col);
1665 }
1666 }
1667 Value::Error { error, .. } => return Err(*error.clone()),
1668 _ => {
1669 return Err(ShellError::UnsupportedInput {
1670 msg: "expected table or record".into(),
1671 input: format!("input type: {:?}", val.get_type()),
1672 msg_span: head_span,
1673 input_span: *span,
1674 });
1675 }
1676 }
1677 }
1678 }
1679 }
1680 Value::Record { val: record, .. } => {
1681 let record = record.to_mut();
1682 if let Some(val) = record.cased_mut(*casing).get_mut(col_name) {
1683 if path.is_empty() {
1684 return Err(ShellError::ColumnAlreadyExists {
1685 col_name: col_name.clone(),
1686 span: *span,
1687 src_span: v_span,
1688 });
1689 } else {
1690 val.insert_data_at_cell_path(path, new_val, head_span)?;
1691 }
1692 } else {
1693 let new_col = Value::with_data_at_cell_path(path, new_val)?;
1694 record.push(col_name, new_col);
1695 }
1696 }
1697 other => {
1698 return Err(ShellError::UnsupportedInput {
1699 msg: "table or record".into(),
1700 input: format!("input type: {:?}", other.get_type()),
1701 msg_span: head_span,
1702 input_span: *span,
1703 });
1704 }
1705 },
1706 PathMember::Int {
1707 val: row_num, span, ..
1708 } => match self {
1709 Value::List { vals, .. } => {
1710 if let Some(v) = vals.get_mut(*row_num) {
1711 if path.is_empty() {
1712 vals.insert(*row_num, new_val);
1713 } else {
1714 v.insert_data_at_cell_path(path, new_val, head_span)?;
1715 }
1716 } else if vals.len() != *row_num {
1717 return Err(ShellError::InsertAfterNextFreeIndex {
1718 available_idx: vals.len(),
1719 span: *span,
1720 });
1721 } else {
1722 vals.push(Value::with_data_at_cell_path(path, new_val)?);
1724 }
1725 }
1726 _ => {
1727 return Err(ShellError::NotAList {
1728 dst_span: *span,
1729 src_span: v_span,
1730 });
1731 }
1732 },
1733 }
1734 } else {
1735 *self = new_val;
1736 }
1737 Ok(())
1738 }
1739
1740 fn with_data_at_cell_path(cell_path: &[PathMember], value: Value) -> Result<Value, ShellError> {
1743 if let Some((member, path)) = cell_path.split_first() {
1744 let span = value.span();
1745 match member {
1746 PathMember::String { val, .. } => Ok(Value::record(
1747 std::iter::once((val.clone(), Value::with_data_at_cell_path(path, value)?))
1748 .collect(),
1749 span,
1750 )),
1751 PathMember::Int { val, .. } => {
1752 if *val == 0usize {
1753 Ok(Value::list(
1754 vec![Value::with_data_at_cell_path(path, value)?],
1755 span,
1756 ))
1757 } else {
1758 Err(ShellError::InsertAfterNextFreeIndex {
1759 available_idx: 0,
1760 span,
1761 })
1762 }
1763 }
1764 }
1765 } else {
1766 Ok(value)
1767 }
1768 }
1769
1770 pub fn recurse_mut<E>(
1777 &mut self,
1778 f: &mut impl FnMut(&mut Value) -> Result<(), E>,
1779 ) -> Result<(), E> {
1780 f(self)?;
1782 match self {
1784 Value::Record { val, .. } => val
1785 .to_mut()
1786 .iter_mut()
1787 .try_for_each(|(_, rec_value)| rec_value.recurse_mut(f)),
1788 Value::List { vals, .. } => vals
1789 .iter_mut()
1790 .try_for_each(|list_value| list_value.recurse_mut(f)),
1791 Value::Closure { val, .. } => val
1794 .captures
1795 .iter_mut()
1796 .map(|(_, captured_value)| captured_value)
1797 .try_for_each(|captured_value| captured_value.recurse_mut(f)),
1798 Value::Bool { .. }
1800 | Value::Int { .. }
1801 | Value::Float { .. }
1802 | Value::Filesize { .. }
1803 | Value::Duration { .. }
1804 | Value::Date { .. }
1805 | Value::Range { .. }
1806 | Value::String { .. }
1807 | Value::Glob { .. }
1808 | Value::Nothing { .. }
1809 | Value::Error { .. }
1810 | Value::Binary { .. }
1811 | Value::CellPath { .. } => Ok(()),
1812 Value::Custom { .. } => Ok(()),
1814 }
1815 }
1816
1817 pub fn is_empty(&self) -> bool {
1819 match self {
1820 Value::String { val, .. } => val.is_empty(),
1821 Value::List { vals, .. } => vals.is_empty(),
1822 Value::Record { val, .. } => val.is_empty(),
1823 Value::Binary { val, .. } => val.is_empty(),
1824 Value::Nothing { .. } => true,
1825 _ => false,
1826 }
1827 }
1828
1829 pub fn is_nothing(&self) -> bool {
1830 matches!(self, Value::Nothing { .. })
1831 }
1832
1833 pub fn is_error(&self) -> bool {
1834 matches!(self, Value::Error { .. })
1835 }
1836
1837 pub fn unwrap_error(self) -> Result<Self, ShellError> {
1839 match self {
1840 Self::Error { error, .. } => Err(*error),
1841 val => Ok(val),
1842 }
1843 }
1844
1845 pub fn is_true(&self) -> bool {
1846 matches!(self, Value::Bool { val: true, .. })
1847 }
1848
1849 pub fn is_false(&self) -> bool {
1850 matches!(self, Value::Bool { val: false, .. })
1851 }
1852
1853 pub fn columns(&self) -> impl Iterator<Item = &String> {
1854 let opt = match self {
1855 Value::Record { val, .. } => Some(val.columns()),
1856 _ => None,
1857 };
1858
1859 opt.into_iter().flatten()
1860 }
1861
1862 pub fn memory_size(&self) -> usize {
1864 match self {
1865 Value::Bool { .. } => std::mem::size_of::<Self>(),
1866 Value::Int { .. } => std::mem::size_of::<Self>(),
1867 Value::Float { .. } => std::mem::size_of::<Self>(),
1868 Value::Filesize { .. } => std::mem::size_of::<Self>(),
1869 Value::Duration { .. } => std::mem::size_of::<Self>(),
1870 Value::Date { .. } => std::mem::size_of::<Self>(),
1871 Value::Range { val, .. } => std::mem::size_of::<Self>() + val.memory_size(),
1872 Value::String { val, .. } => std::mem::size_of::<Self>() + val.capacity(),
1873 Value::Glob { val, .. } => std::mem::size_of::<Self>() + val.capacity(),
1874 Value::Record { val, .. } => std::mem::size_of::<Self>() + val.memory_size(),
1875 Value::List { vals, .. } => {
1876 std::mem::size_of::<Self>() + vals.iter().map(|v| v.memory_size()).sum::<usize>()
1877 }
1878 Value::Closure { val, .. } => std::mem::size_of::<Self>() + val.memory_size(),
1879 Value::Nothing { .. } => std::mem::size_of::<Self>(),
1880 Value::Error { error, .. } => {
1881 std::mem::size_of::<Self>() + std::mem::size_of_val(error)
1882 }
1883 Value::Binary { val, .. } => std::mem::size_of::<Self>() + val.capacity(),
1884 Value::CellPath { val, .. } => std::mem::size_of::<Self>() + val.memory_size(),
1885 Value::Custom { val, .. } => std::mem::size_of::<Self>() + val.memory_size(),
1886 }
1887 }
1888
1889 pub fn bool(val: bool, span: Span) -> Value {
1890 Value::Bool {
1891 val,
1892 internal_span: span,
1893 }
1894 }
1895
1896 pub fn int(val: i64, span: Span) -> Value {
1897 Value::Int {
1898 val,
1899 internal_span: span,
1900 }
1901 }
1902
1903 pub fn float(val: f64, span: Span) -> Value {
1904 Value::Float {
1905 val,
1906 internal_span: span,
1907 }
1908 }
1909
1910 pub fn filesize(val: impl Into<Filesize>, span: Span) -> Value {
1911 Value::Filesize {
1912 val: val.into(),
1913 internal_span: span,
1914 }
1915 }
1916
1917 pub fn duration(val: i64, span: Span) -> Value {
1918 Value::Duration {
1919 val,
1920 internal_span: span,
1921 }
1922 }
1923
1924 pub fn date(val: DateTime<FixedOffset>, span: Span) -> Value {
1925 Value::Date {
1926 val,
1927 internal_span: span,
1928 }
1929 }
1930
1931 pub fn range(val: Range, span: Span) -> Value {
1932 Value::Range {
1933 val: val.into(),
1934 signals: None,
1935 internal_span: span,
1936 }
1937 }
1938
1939 pub fn string(val: impl Into<String>, span: Span) -> Value {
1940 Value::String {
1941 val: val.into(),
1942 internal_span: span,
1943 }
1944 }
1945
1946 pub fn glob(val: impl Into<String>, no_expand: bool, span: Span) -> Value {
1947 Value::Glob {
1948 val: val.into(),
1949 no_expand,
1950 internal_span: span,
1951 }
1952 }
1953
1954 pub fn record(val: Record, span: Span) -> Value {
1955 Value::Record {
1956 val: SharedCow::new(val),
1957 internal_span: span,
1958 }
1959 }
1960
1961 pub fn list(vals: Vec<Value>, span: Span) -> Value {
1962 Value::List {
1963 vals,
1964 signals: None,
1965 internal_span: span,
1966 }
1967 }
1968
1969 pub fn closure(val: Closure, span: Span) -> Value {
1970 Value::Closure {
1971 val: val.into(),
1972 internal_span: span,
1973 }
1974 }
1975
1976 pub fn nothing(span: Span) -> Value {
1978 Value::Nothing {
1979 internal_span: span,
1980 }
1981 }
1982
1983 pub fn error(error: ShellError, span: Span) -> Value {
1984 Value::Error {
1985 error: Box::new(error),
1986 internal_span: span,
1987 }
1988 }
1989
1990 pub fn binary(val: impl Into<Vec<u8>>, span: Span) -> Value {
1991 Value::Binary {
1992 val: val.into(),
1993 internal_span: span,
1994 }
1995 }
1996
1997 pub fn cell_path(val: CellPath, span: Span) -> Value {
1998 Value::CellPath {
1999 val,
2000 internal_span: span,
2001 }
2002 }
2003
2004 pub fn custom(val: Box<dyn CustomValue>, span: Span) -> Value {
2005 Value::Custom {
2006 val,
2007 internal_span: span,
2008 }
2009 }
2010
2011 pub fn test_bool(val: bool) -> Value {
2014 Value::bool(val, Span::test_data())
2015 }
2016
2017 pub fn test_int(val: i64) -> Value {
2020 Value::int(val, Span::test_data())
2021 }
2022
2023 pub fn test_float(val: f64) -> Value {
2026 Value::float(val, Span::test_data())
2027 }
2028
2029 pub fn test_filesize(val: impl Into<Filesize>) -> Value {
2032 Value::filesize(val, Span::test_data())
2033 }
2034
2035 pub fn test_duration(val: i64) -> Value {
2038 Value::duration(val, Span::test_data())
2039 }
2040
2041 pub fn test_date(val: DateTime<FixedOffset>) -> Value {
2044 Value::date(val, Span::test_data())
2045 }
2046
2047 pub fn test_range(val: Range) -> Value {
2050 Value::range(val, Span::test_data())
2051 }
2052
2053 pub fn test_string(val: impl Into<String>) -> Value {
2056 Value::string(val, Span::test_data())
2057 }
2058
2059 pub fn test_glob(val: impl Into<String>) -> Value {
2062 Value::glob(val, false, Span::test_data())
2063 }
2064
2065 pub fn test_record(val: Record) -> Value {
2068 Value::record(val, Span::test_data())
2069 }
2070
2071 pub fn test_list(vals: Vec<Value>) -> Value {
2074 Value::list(vals, Span::test_data())
2075 }
2076
2077 pub fn test_closure(val: Closure) -> Value {
2080 Value::closure(val, Span::test_data())
2081 }
2082
2083 pub fn test_nothing() -> Value {
2086 Value::nothing(Span::test_data())
2087 }
2088
2089 pub fn test_binary(val: impl Into<Vec<u8>>) -> Value {
2092 Value::binary(val, Span::test_data())
2093 }
2094
2095 pub fn test_cell_path(val: CellPath) -> Value {
2098 Value::cell_path(val, Span::test_data())
2099 }
2100
2101 pub fn test_custom_value(val: Box<dyn CustomValue>) -> Value {
2104 Value::custom(val, Span::test_data())
2105 }
2106
2107 pub fn test_values() -> Vec<Value> {
2113 vec![
2114 Value::test_bool(false),
2115 Value::test_int(0),
2116 Value::test_filesize(0),
2117 Value::test_duration(0),
2118 Value::test_date(DateTime::UNIX_EPOCH.into()),
2119 Value::test_range(Range::IntRange(IntRange {
2120 start: 0,
2121 step: 1,
2122 end: Bound::Excluded(0),
2123 })),
2124 Value::test_float(0.0),
2125 Value::test_string(String::new()),
2126 Value::test_record(Record::new()),
2127 Value::test_list(Vec::new()),
2128 Value::test_closure(Closure {
2129 block_id: BlockId::new(0),
2130 captures: Vec::new(),
2131 }),
2132 Value::test_nothing(),
2133 Value::error(
2134 ShellError::NushellFailed { msg: String::new() },
2135 Span::test_data(),
2136 ),
2137 Value::test_binary(Vec::new()),
2138 Value::test_cell_path(CellPath {
2139 members: Vec::new(),
2140 }),
2141 ]
2143 }
2144
2145 #[track_caller]
2151 pub fn assert_eq(&self, other: impl IntoValue) {
2152 let other = other.into_value(Span::test_data());
2153 assert_eq!(self, &other)
2154 }
2155
2156 pub fn inject_signals(&mut self, engine_state: &EngineState) {
2159 match self {
2160 Value::List { signals: s, .. } | Value::Range { signals: s, .. } => {
2161 *s = Some(engine_state.signals().clone());
2162 }
2163 _ => (),
2164 }
2165 }
2166}
2167
2168fn get_value_member<'a>(
2169 current: &'a Value,
2170 member: &PathMember,
2171) -> Result<ControlFlow<Span, Cow<'a, Value>>, ShellError> {
2172 match member {
2173 PathMember::Int {
2174 val: count,
2175 span: origin_span,
2176 optional,
2177 } => {
2178 match current {
2180 Value::List { vals, .. } => {
2181 if *count < vals.len() {
2182 Ok(ControlFlow::Continue(Cow::Borrowed(&vals[*count])))
2183 } else if *optional {
2184 Ok(ControlFlow::Break(*origin_span))
2185 } else if vals.is_empty() {
2187 Err(ShellError::AccessEmptyContent { span: *origin_span })
2188 } else {
2189 Err(ShellError::AccessBeyondEnd {
2190 max_idx: vals.len() - 1,
2191 span: *origin_span,
2192 })
2193 }
2194 }
2195 Value::Binary { val, .. } => {
2196 if let Some(item) = val.get(*count) {
2197 Ok(ControlFlow::Continue(Cow::Owned(Value::int(
2198 *item as i64,
2199 *origin_span,
2200 ))))
2201 } else if *optional {
2202 Ok(ControlFlow::Break(*origin_span))
2203 } else if val.is_empty() {
2205 Err(ShellError::AccessEmptyContent { span: *origin_span })
2206 } else {
2207 Err(ShellError::AccessBeyondEnd {
2208 max_idx: val.len() - 1,
2209 span: *origin_span,
2210 })
2211 }
2212 }
2213 Value::Range { val, .. } => {
2214 if let Some(item) = val
2215 .into_range_iter(current.span(), Signals::empty())
2216 .nth(*count)
2217 {
2218 Ok(ControlFlow::Continue(Cow::Owned(item)))
2219 } else if *optional {
2220 Ok(ControlFlow::Break(*origin_span))
2221 } else {
2223 Err(ShellError::AccessBeyondEndOfStream {
2224 span: *origin_span,
2225 })
2226 }
2227 }
2228 Value::Custom { val, .. } => {
2229 match val.follow_path_int(current.span(), *count, *origin_span, *optional)
2230 {
2231 Ok(val) => Ok(ControlFlow::Continue(Cow::Owned(val))),
2232 Err(err) => {
2233 if *optional {
2234 Ok(ControlFlow::Break(*origin_span))
2235 } else {
2237 Err(err)
2238 }
2239 }
2240 }
2241 }
2242 Value::Nothing { .. } if *optional => Ok(ControlFlow::Break(*origin_span)),
2243 Value::Record { .. } => Err(ShellError::TypeMismatch {
2246 err_message:"Can't access record values with a row index. Try specifying a column name instead".into(),
2247 span: *origin_span,
2248 }),
2249 Value::Error { error, .. } => Err(*error.clone()),
2250 x => Err(ShellError::IncompatiblePathAccess { type_name: format!("{}", x.get_type()), span: *origin_span }),
2251 }
2252 }
2253 PathMember::String {
2254 val: column_name,
2255 span: origin_span,
2256 optional,
2257 casing,
2258 } => {
2259 let span = current.span();
2260 match current {
2261 Value::Record { val, .. } => {
2262 let found = val.cased(*casing).get(column_name);
2263 if let Some(found) = found {
2264 Ok(ControlFlow::Continue(Cow::Borrowed(found)))
2265 } else if *optional {
2266 Ok(ControlFlow::Break(*origin_span))
2267 } else if let Some(suggestion) = did_you_mean(val.columns(), column_name) {
2269 Err(ShellError::DidYouMean {
2270 suggestion,
2271 span: *origin_span,
2272 })
2273 } else {
2274 Err(ShellError::CantFindColumn {
2275 col_name: column_name.clone(),
2276 span: Some(*origin_span),
2277 src_span: span,
2278 })
2279 }
2280 }
2281 Value::List { vals, .. } => {
2285 let list = vals
2286 .iter()
2287 .map(|val| {
2288 let val_span = val.span();
2289 match val {
2290 Value::Record { val, .. } => {
2291 let found = val.cased(*casing).get(column_name);
2292 if let Some(found) = found {
2293 Ok(found.clone())
2294 } else if *optional {
2295 Ok(Value::nothing(*origin_span))
2296 } else if let Some(suggestion) =
2297 did_you_mean(val.columns(), column_name)
2298 {
2299 Err(ShellError::DidYouMean {
2300 suggestion,
2301 span: *origin_span,
2302 })
2303 } else {
2304 Err(ShellError::CantFindColumn {
2305 col_name: column_name.clone(),
2306 span: Some(*origin_span),
2307 src_span: val_span,
2308 })
2309 }
2310 }
2311 Value::Nothing { .. } if *optional => {
2312 Ok(Value::nothing(*origin_span))
2313 }
2314 _ => Err(ShellError::CantFindColumn {
2315 col_name: column_name.clone(),
2316 span: Some(*origin_span),
2317 src_span: val_span,
2318 }),
2319 }
2320 })
2321 .collect::<Result<_, _>>()?;
2322
2323 Ok(ControlFlow::Continue(Cow::Owned(Value::list(list, span))))
2324 }
2325 Value::Custom { val, .. } => {
2326 match val.follow_path_string(
2327 current.span(),
2328 column_name.clone(),
2329 *origin_span,
2330 *optional,
2331 *casing,
2332 ) {
2333 Ok(val) => Ok(ControlFlow::Continue(Cow::Owned(val))),
2334 Err(err) => {
2335 if *optional {
2336 Ok(ControlFlow::Break(*origin_span))
2337 } else {
2339 Err(err)
2340 }
2341 }
2342 }
2343 }
2344 Value::Nothing { .. } if *optional => Ok(ControlFlow::Break(*origin_span)),
2345 Value::Error { error, .. } => Err(error.as_ref().clone()),
2346 x => Err(ShellError::IncompatiblePathAccess {
2347 type_name: format!("{}", x.get_type()),
2348 span: *origin_span,
2349 }),
2350 }
2351 }
2352 }
2353}
2354
2355impl Default for Value {
2356 fn default() -> Self {
2357 Value::Nothing {
2358 internal_span: Span::unknown(),
2359 }
2360 }
2361}
2362
2363impl PartialOrd for Value {
2364 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
2365 fn compare_floats(val: f64, other: f64) -> Option<Ordering> {
2371 let prec = f64::EPSILON.max(val.abs().max(other.abs()) * f64::EPSILON);
2372
2373 if (other - val).abs() <= prec {
2374 return Some(Ordering::Equal);
2375 }
2376
2377 val.partial_cmp(&other)
2378 }
2379
2380 match (self, other) {
2381 (Value::Bool { val: lhs, .. }, rhs) => match rhs {
2382 Value::Bool { val: rhs, .. } => lhs.partial_cmp(rhs),
2383 Value::Int { .. } => Some(Ordering::Less),
2384 Value::Float { .. } => Some(Ordering::Less),
2385 Value::String { .. } => Some(Ordering::Less),
2386 Value::Glob { .. } => Some(Ordering::Less),
2387 Value::Filesize { .. } => Some(Ordering::Less),
2388 Value::Duration { .. } => Some(Ordering::Less),
2389 Value::Date { .. } => Some(Ordering::Less),
2390 Value::Range { .. } => Some(Ordering::Less),
2391 Value::Record { .. } => Some(Ordering::Less),
2392 Value::List { .. } => Some(Ordering::Less),
2393 Value::Closure { .. } => Some(Ordering::Less),
2394 Value::Error { .. } => Some(Ordering::Less),
2395 Value::Binary { .. } => Some(Ordering::Less),
2396 Value::CellPath { .. } => Some(Ordering::Less),
2397 Value::Custom { .. } => Some(Ordering::Less),
2398 Value::Nothing { .. } => Some(Ordering::Less),
2399 },
2400 (Value::Int { val: lhs, .. }, rhs) => match rhs {
2401 Value::Bool { .. } => Some(Ordering::Greater),
2402 Value::Int { val: rhs, .. } => lhs.partial_cmp(rhs),
2403 Value::Float { val: rhs, .. } => compare_floats(*lhs as f64, *rhs),
2404 Value::String { .. } => Some(Ordering::Less),
2405 Value::Glob { .. } => Some(Ordering::Less),
2406 Value::Filesize { .. } => Some(Ordering::Less),
2407 Value::Duration { .. } => Some(Ordering::Less),
2408 Value::Date { .. } => Some(Ordering::Less),
2409 Value::Range { .. } => Some(Ordering::Less),
2410 Value::Record { .. } => Some(Ordering::Less),
2411 Value::List { .. } => Some(Ordering::Less),
2412 Value::Closure { .. } => Some(Ordering::Less),
2413 Value::Error { .. } => Some(Ordering::Less),
2414 Value::Binary { .. } => Some(Ordering::Less),
2415 Value::CellPath { .. } => Some(Ordering::Less),
2416 Value::Custom { .. } => Some(Ordering::Less),
2417 Value::Nothing { .. } => Some(Ordering::Less),
2418 },
2419 (Value::Float { val: lhs, .. }, rhs) => match rhs {
2420 Value::Bool { .. } => Some(Ordering::Greater),
2421 Value::Int { val: rhs, .. } => compare_floats(*lhs, *rhs as f64),
2422 Value::Float { val: rhs, .. } => compare_floats(*lhs, *rhs),
2423 Value::String { .. } => Some(Ordering::Less),
2424 Value::Glob { .. } => Some(Ordering::Less),
2425 Value::Filesize { .. } => Some(Ordering::Less),
2426 Value::Duration { .. } => Some(Ordering::Less),
2427 Value::Date { .. } => Some(Ordering::Less),
2428 Value::Range { .. } => Some(Ordering::Less),
2429 Value::Record { .. } => Some(Ordering::Less),
2430 Value::List { .. } => Some(Ordering::Less),
2431 Value::Closure { .. } => Some(Ordering::Less),
2432 Value::Error { .. } => Some(Ordering::Less),
2433 Value::Binary { .. } => Some(Ordering::Less),
2434 Value::CellPath { .. } => Some(Ordering::Less),
2435 Value::Custom { .. } => Some(Ordering::Less),
2436 Value::Nothing { .. } => Some(Ordering::Less),
2437 },
2438 (Value::String { val: lhs, .. }, rhs) => match rhs {
2439 Value::Bool { .. } => Some(Ordering::Greater),
2440 Value::Int { .. } => Some(Ordering::Greater),
2441 Value::Float { .. } => Some(Ordering::Greater),
2442 Value::String { val: rhs, .. } => lhs.partial_cmp(rhs),
2443 Value::Glob { val: rhs, .. } => lhs.partial_cmp(rhs),
2444 Value::Filesize { .. } => Some(Ordering::Less),
2445 Value::Duration { .. } => Some(Ordering::Less),
2446 Value::Date { .. } => Some(Ordering::Less),
2447 Value::Range { .. } => Some(Ordering::Less),
2448 Value::Record { .. } => Some(Ordering::Less),
2449 Value::List { .. } => Some(Ordering::Less),
2450 Value::Closure { .. } => Some(Ordering::Less),
2451 Value::Error { .. } => Some(Ordering::Less),
2452 Value::Binary { .. } => Some(Ordering::Less),
2453 Value::CellPath { .. } => Some(Ordering::Less),
2454 Value::Custom { .. } => Some(Ordering::Less),
2455 Value::Nothing { .. } => Some(Ordering::Less),
2456 },
2457 (Value::Glob { val: lhs, .. }, rhs) => match rhs {
2458 Value::Bool { .. } => Some(Ordering::Greater),
2459 Value::Int { .. } => Some(Ordering::Greater),
2460 Value::Float { .. } => Some(Ordering::Greater),
2461 Value::String { val: rhs, .. } => lhs.partial_cmp(rhs),
2462 Value::Glob { val: rhs, .. } => lhs.partial_cmp(rhs),
2463 Value::Filesize { .. } => Some(Ordering::Less),
2464 Value::Duration { .. } => Some(Ordering::Less),
2465 Value::Date { .. } => Some(Ordering::Less),
2466 Value::Range { .. } => Some(Ordering::Less),
2467 Value::Record { .. } => Some(Ordering::Less),
2468 Value::List { .. } => Some(Ordering::Less),
2469 Value::Closure { .. } => Some(Ordering::Less),
2470 Value::Error { .. } => Some(Ordering::Less),
2471 Value::Binary { .. } => Some(Ordering::Less),
2472 Value::CellPath { .. } => Some(Ordering::Less),
2473 Value::Custom { .. } => Some(Ordering::Less),
2474 Value::Nothing { .. } => Some(Ordering::Less),
2475 },
2476 (Value::Filesize { val: lhs, .. }, rhs) => match rhs {
2477 Value::Bool { .. } => Some(Ordering::Greater),
2478 Value::Int { .. } => Some(Ordering::Greater),
2479 Value::Float { .. } => Some(Ordering::Greater),
2480 Value::String { .. } => Some(Ordering::Greater),
2481 Value::Glob { .. } => Some(Ordering::Greater),
2482 Value::Filesize { val: rhs, .. } => lhs.partial_cmp(rhs),
2483 Value::Duration { .. } => Some(Ordering::Less),
2484 Value::Date { .. } => Some(Ordering::Less),
2485 Value::Range { .. } => Some(Ordering::Less),
2486 Value::Record { .. } => Some(Ordering::Less),
2487 Value::List { .. } => Some(Ordering::Less),
2488 Value::Closure { .. } => Some(Ordering::Less),
2489 Value::Error { .. } => Some(Ordering::Less),
2490 Value::Binary { .. } => Some(Ordering::Less),
2491 Value::CellPath { .. } => Some(Ordering::Less),
2492 Value::Custom { .. } => Some(Ordering::Less),
2493 Value::Nothing { .. } => Some(Ordering::Less),
2494 },
2495 (Value::Duration { val: lhs, .. }, rhs) => match rhs {
2496 Value::Bool { .. } => Some(Ordering::Greater),
2497 Value::Int { .. } => Some(Ordering::Greater),
2498 Value::Float { .. } => Some(Ordering::Greater),
2499 Value::String { .. } => Some(Ordering::Greater),
2500 Value::Glob { .. } => Some(Ordering::Greater),
2501 Value::Filesize { .. } => Some(Ordering::Greater),
2502 Value::Duration { val: rhs, .. } => lhs.partial_cmp(rhs),
2503 Value::Date { .. } => Some(Ordering::Less),
2504 Value::Range { .. } => Some(Ordering::Less),
2505 Value::Record { .. } => Some(Ordering::Less),
2506 Value::List { .. } => Some(Ordering::Less),
2507 Value::Closure { .. } => Some(Ordering::Less),
2508 Value::Error { .. } => Some(Ordering::Less),
2509 Value::Binary { .. } => Some(Ordering::Less),
2510 Value::CellPath { .. } => Some(Ordering::Less),
2511 Value::Custom { .. } => Some(Ordering::Less),
2512 Value::Nothing { .. } => Some(Ordering::Less),
2513 },
2514 (Value::Date { val: lhs, .. }, rhs) => match rhs {
2515 Value::Bool { .. } => Some(Ordering::Greater),
2516 Value::Int { .. } => Some(Ordering::Greater),
2517 Value::Float { .. } => Some(Ordering::Greater),
2518 Value::String { .. } => Some(Ordering::Greater),
2519 Value::Glob { .. } => Some(Ordering::Greater),
2520 Value::Filesize { .. } => Some(Ordering::Greater),
2521 Value::Duration { .. } => Some(Ordering::Greater),
2522 Value::Date { val: rhs, .. } => lhs.partial_cmp(rhs),
2523 Value::Range { .. } => Some(Ordering::Less),
2524 Value::Record { .. } => Some(Ordering::Less),
2525 Value::List { .. } => Some(Ordering::Less),
2526 Value::Closure { .. } => Some(Ordering::Less),
2527 Value::Error { .. } => Some(Ordering::Less),
2528 Value::Binary { .. } => Some(Ordering::Less),
2529 Value::CellPath { .. } => Some(Ordering::Less),
2530 Value::Custom { .. } => Some(Ordering::Less),
2531 Value::Nothing { .. } => Some(Ordering::Less),
2532 },
2533 (Value::Range { val: lhs, .. }, rhs) => match rhs {
2534 Value::Bool { .. } => Some(Ordering::Greater),
2535 Value::Int { .. } => Some(Ordering::Greater),
2536 Value::Float { .. } => Some(Ordering::Greater),
2537 Value::String { .. } => Some(Ordering::Greater),
2538 Value::Glob { .. } => Some(Ordering::Greater),
2539 Value::Filesize { .. } => Some(Ordering::Greater),
2540 Value::Duration { .. } => Some(Ordering::Greater),
2541 Value::Date { .. } => Some(Ordering::Greater),
2542 Value::Range { val: rhs, .. } => lhs.partial_cmp(rhs),
2543 Value::Record { .. } => Some(Ordering::Less),
2544 Value::List { .. } => Some(Ordering::Less),
2545 Value::Closure { .. } => Some(Ordering::Less),
2546 Value::Error { .. } => Some(Ordering::Less),
2547 Value::Binary { .. } => Some(Ordering::Less),
2548 Value::CellPath { .. } => Some(Ordering::Less),
2549 Value::Custom { .. } => Some(Ordering::Less),
2550 Value::Nothing { .. } => Some(Ordering::Less),
2551 },
2552 (Value::Record { val: lhs, .. }, rhs) => match rhs {
2553 Value::Bool { .. } => Some(Ordering::Greater),
2554 Value::Int { .. } => Some(Ordering::Greater),
2555 Value::Float { .. } => Some(Ordering::Greater),
2556 Value::String { .. } => Some(Ordering::Greater),
2557 Value::Glob { .. } => Some(Ordering::Greater),
2558 Value::Filesize { .. } => Some(Ordering::Greater),
2559 Value::Duration { .. } => Some(Ordering::Greater),
2560 Value::Date { .. } => Some(Ordering::Greater),
2561 Value::Range { .. } => Some(Ordering::Greater),
2562 Value::Record { val: rhs, .. } => {
2563 let mut lhs = lhs.clone().into_owned();
2567 let mut rhs = rhs.clone().into_owned();
2568 lhs.sort_cols();
2569 rhs.sort_cols();
2570
2571 for (a, b) in lhs.columns().zip(rhs.columns()) {
2573 let result = a.partial_cmp(b);
2574 if result != Some(Ordering::Equal) {
2575 return result;
2576 }
2577 }
2578 for (a, b) in lhs.values().zip(rhs.values()) {
2580 let result = a.partial_cmp(b);
2581 if result != Some(Ordering::Equal) {
2582 return result;
2583 }
2584 }
2585 lhs.len().partial_cmp(&rhs.len())
2588 }
2589 Value::List { .. } => Some(Ordering::Less),
2590 Value::Closure { .. } => Some(Ordering::Less),
2591 Value::Error { .. } => Some(Ordering::Less),
2592 Value::Binary { .. } => Some(Ordering::Less),
2593 Value::CellPath { .. } => Some(Ordering::Less),
2594 Value::Custom { .. } => Some(Ordering::Less),
2595 Value::Nothing { .. } => Some(Ordering::Less),
2596 },
2597 (Value::List { vals: lhs, .. }, rhs) => match rhs {
2598 Value::Bool { .. } => Some(Ordering::Greater),
2599 Value::Int { .. } => Some(Ordering::Greater),
2600 Value::Float { .. } => Some(Ordering::Greater),
2601 Value::String { .. } => Some(Ordering::Greater),
2602 Value::Glob { .. } => Some(Ordering::Greater),
2603 Value::Filesize { .. } => Some(Ordering::Greater),
2604 Value::Duration { .. } => Some(Ordering::Greater),
2605 Value::Date { .. } => Some(Ordering::Greater),
2606 Value::Range { .. } => Some(Ordering::Greater),
2607 Value::Record { .. } => Some(Ordering::Greater),
2608 Value::List { vals: rhs, .. } => lhs.partial_cmp(rhs),
2609 Value::Closure { .. } => Some(Ordering::Less),
2610 Value::Error { .. } => Some(Ordering::Less),
2611 Value::Binary { .. } => Some(Ordering::Less),
2612 Value::CellPath { .. } => Some(Ordering::Less),
2613 Value::Custom { .. } => Some(Ordering::Less),
2614 Value::Nothing { .. } => Some(Ordering::Less),
2615 },
2616 (Value::Closure { val: lhs, .. }, rhs) => match rhs {
2617 Value::Bool { .. } => Some(Ordering::Greater),
2618 Value::Int { .. } => Some(Ordering::Greater),
2619 Value::Float { .. } => Some(Ordering::Greater),
2620 Value::String { .. } => Some(Ordering::Greater),
2621 Value::Glob { .. } => Some(Ordering::Greater),
2622 Value::Filesize { .. } => Some(Ordering::Greater),
2623 Value::Duration { .. } => Some(Ordering::Greater),
2624 Value::Date { .. } => Some(Ordering::Greater),
2625 Value::Range { .. } => Some(Ordering::Greater),
2626 Value::Record { .. } => Some(Ordering::Greater),
2627 Value::List { .. } => Some(Ordering::Greater),
2628 Value::Closure { val: rhs, .. } => lhs.block_id.partial_cmp(&rhs.block_id),
2629 Value::Error { .. } => Some(Ordering::Less),
2630 Value::Binary { .. } => Some(Ordering::Less),
2631 Value::CellPath { .. } => Some(Ordering::Less),
2632 Value::Custom { .. } => Some(Ordering::Less),
2633 Value::Nothing { .. } => Some(Ordering::Less),
2634 },
2635 (Value::Error { .. }, rhs) => match rhs {
2636 Value::Bool { .. } => Some(Ordering::Greater),
2637 Value::Int { .. } => Some(Ordering::Greater),
2638 Value::Float { .. } => Some(Ordering::Greater),
2639 Value::String { .. } => Some(Ordering::Greater),
2640 Value::Glob { .. } => Some(Ordering::Greater),
2641 Value::Filesize { .. } => Some(Ordering::Greater),
2642 Value::Duration { .. } => Some(Ordering::Greater),
2643 Value::Date { .. } => Some(Ordering::Greater),
2644 Value::Range { .. } => Some(Ordering::Greater),
2645 Value::Record { .. } => Some(Ordering::Greater),
2646 Value::List { .. } => Some(Ordering::Greater),
2647 Value::Closure { .. } => Some(Ordering::Greater),
2648 Value::Error { .. } => Some(Ordering::Equal),
2649 Value::Binary { .. } => Some(Ordering::Less),
2650 Value::CellPath { .. } => Some(Ordering::Less),
2651 Value::Custom { .. } => Some(Ordering::Less),
2652 Value::Nothing { .. } => Some(Ordering::Less),
2653 },
2654 (Value::Binary { val: lhs, .. }, rhs) => match rhs {
2655 Value::Bool { .. } => Some(Ordering::Greater),
2656 Value::Int { .. } => Some(Ordering::Greater),
2657 Value::Float { .. } => Some(Ordering::Greater),
2658 Value::String { .. } => Some(Ordering::Greater),
2659 Value::Glob { .. } => Some(Ordering::Greater),
2660 Value::Filesize { .. } => Some(Ordering::Greater),
2661 Value::Duration { .. } => Some(Ordering::Greater),
2662 Value::Date { .. } => Some(Ordering::Greater),
2663 Value::Range { .. } => Some(Ordering::Greater),
2664 Value::Record { .. } => Some(Ordering::Greater),
2665 Value::List { .. } => Some(Ordering::Greater),
2666 Value::Closure { .. } => Some(Ordering::Greater),
2667 Value::Error { .. } => Some(Ordering::Greater),
2668 Value::Binary { val: rhs, .. } => lhs.partial_cmp(rhs),
2669 Value::CellPath { .. } => Some(Ordering::Less),
2670 Value::Custom { .. } => Some(Ordering::Less),
2671 Value::Nothing { .. } => Some(Ordering::Less),
2672 },
2673 (Value::CellPath { val: lhs, .. }, rhs) => match rhs {
2674 Value::Bool { .. } => Some(Ordering::Greater),
2675 Value::Int { .. } => Some(Ordering::Greater),
2676 Value::Float { .. } => Some(Ordering::Greater),
2677 Value::String { .. } => Some(Ordering::Greater),
2678 Value::Glob { .. } => Some(Ordering::Greater),
2679 Value::Filesize { .. } => Some(Ordering::Greater),
2680 Value::Duration { .. } => Some(Ordering::Greater),
2681 Value::Date { .. } => Some(Ordering::Greater),
2682 Value::Range { .. } => Some(Ordering::Greater),
2683 Value::Record { .. } => Some(Ordering::Greater),
2684 Value::List { .. } => Some(Ordering::Greater),
2685 Value::Closure { .. } => Some(Ordering::Greater),
2686 Value::Error { .. } => Some(Ordering::Greater),
2687 Value::Binary { .. } => Some(Ordering::Greater),
2688 Value::CellPath { val: rhs, .. } => lhs.partial_cmp(rhs),
2689 Value::Custom { .. } => Some(Ordering::Less),
2690 Value::Nothing { .. } => Some(Ordering::Less),
2691 },
2692 (Value::Custom { val: lhs, .. }, rhs) => lhs.partial_cmp(rhs),
2693 (Value::Nothing { .. }, rhs) => match rhs {
2694 Value::Bool { .. } => Some(Ordering::Greater),
2695 Value::Int { .. } => Some(Ordering::Greater),
2696 Value::Float { .. } => Some(Ordering::Greater),
2697 Value::String { .. } => Some(Ordering::Greater),
2698 Value::Glob { .. } => Some(Ordering::Greater),
2699 Value::Filesize { .. } => Some(Ordering::Greater),
2700 Value::Duration { .. } => Some(Ordering::Greater),
2701 Value::Date { .. } => Some(Ordering::Greater),
2702 Value::Range { .. } => Some(Ordering::Greater),
2703 Value::Record { .. } => Some(Ordering::Greater),
2704 Value::List { .. } => Some(Ordering::Greater),
2705 Value::Closure { .. } => Some(Ordering::Greater),
2706 Value::Error { .. } => Some(Ordering::Greater),
2707 Value::Binary { .. } => Some(Ordering::Greater),
2708 Value::CellPath { .. } => Some(Ordering::Greater),
2709 Value::Custom { .. } => Some(Ordering::Greater),
2710 Value::Nothing { .. } => Some(Ordering::Equal),
2711 },
2712 }
2713 }
2714}
2715
2716impl PartialEq for Value {
2717 fn eq(&self, other: &Self) -> bool {
2718 self.partial_cmp(other).is_some_and(Ordering::is_eq)
2719 }
2720}
2721
2722fn checked_duration_operation<F>(a: i64, b: i64, op: F, span: Span) -> Result<Value, ShellError>
2723where
2724 F: Fn(i64, i64) -> Option<i64>,
2725{
2726 if let Some(val) = op(a, b) {
2727 Ok(Value::duration(val, span))
2728 } else {
2729 Err(ShellError::OperatorOverflow {
2730 msg: "operation overflowed".to_owned(),
2731 span,
2732 help: None,
2733 })
2734 }
2735}
2736
2737impl Value {
2738 pub fn add(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
2739 match (self, rhs) {
2740 (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2741 if let Some(val) = lhs.checked_add(*rhs) {
2742 Ok(Value::int(val, span))
2743 } else {
2744 Err(ShellError::OperatorOverflow {
2745 msg: "add operation overflowed".into(),
2746 span,
2747 help: Some("Consider using floating point values for increased range by promoting operand with 'into float'. Note: float has reduced precision!".into()),
2748 })
2749 }
2750 }
2751 (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2752 Ok(Value::float(*lhs as f64 + *rhs, span))
2753 }
2754 (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2755 Ok(Value::float(*lhs + *rhs as f64, span))
2756 }
2757 (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2758 Ok(Value::float(lhs + rhs, span))
2759 }
2760 (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
2761 Ok(Value::string(lhs.to_string() + rhs, span))
2762 }
2763 (Value::Duration { val: lhs, .. }, Value::Date { val: rhs, .. }) => {
2764 if let Some(val) = rhs.checked_add_signed(chrono::Duration::nanoseconds(*lhs)) {
2765 Ok(Value::date(val, span))
2766 } else {
2767 Err(ShellError::OperatorOverflow {
2768 msg: "addition operation overflowed".into(),
2769 span,
2770 help: None,
2771 })
2772 }
2773 }
2774 (Value::Date { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
2775 if let Some(val) = lhs.checked_add_signed(chrono::Duration::nanoseconds(*rhs)) {
2776 Ok(Value::date(val, span))
2777 } else {
2778 Err(ShellError::OperatorOverflow {
2779 msg: "addition operation overflowed".into(),
2780 span,
2781 help: None,
2782 })
2783 }
2784 }
2785 (Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
2786 checked_duration_operation(*lhs, *rhs, i64::checked_add, span)
2787 }
2788 (Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
2789 if let Some(val) = *lhs + *rhs {
2790 Ok(Value::filesize(val, span))
2791 } else {
2792 Err(ShellError::OperatorOverflow {
2793 msg: "add operation overflowed".into(),
2794 span,
2795 help: None,
2796 })
2797 }
2798 }
2799 (Value::Custom { val: lhs, .. }, rhs) => {
2800 lhs.operation(self.span(), Operator::Math(Math::Add), op, rhs)
2801 }
2802 _ => Err(operator_type_error(
2803 Operator::Math(Math::Add),
2804 op,
2805 self,
2806 rhs,
2807 |val| {
2808 matches!(
2809 val,
2810 Value::Int { .. }
2811 | Value::Float { .. }
2812 | Value::String { .. }
2813 | Value::Date { .. }
2814 | Value::Duration { .. }
2815 | Value::Filesize { .. },
2816 )
2817 },
2818 )),
2819 }
2820 }
2821
2822 pub fn sub(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
2823 match (self, rhs) {
2824 (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2825 if let Some(val) = lhs.checked_sub(*rhs) {
2826 Ok(Value::int(val, span))
2827 } else {
2828 Err(ShellError::OperatorOverflow {
2829 msg: "subtraction operation overflowed".into(),
2830 span,
2831 help: Some("Consider using floating point values for increased range by promoting operand with 'into float'. Note: float has reduced precision!".into()),
2832 })
2833 }
2834 }
2835 (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2836 Ok(Value::float(*lhs as f64 - *rhs, span))
2837 }
2838 (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2839 Ok(Value::float(*lhs - *rhs as f64, span))
2840 }
2841 (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2842 Ok(Value::float(lhs - rhs, span))
2843 }
2844 (Value::Date { val: lhs, .. }, Value::Date { val: rhs, .. }) => {
2845 let result = lhs.signed_duration_since(*rhs);
2846 if let Some(v) = result.num_nanoseconds() {
2847 Ok(Value::duration(v, span))
2848 } else {
2849 Err(ShellError::OperatorOverflow {
2850 msg: "subtraction operation overflowed".into(),
2851 span,
2852 help: None,
2853 })
2854 }
2855 }
2856 (Value::Date { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
2857 match lhs.checked_sub_signed(chrono::Duration::nanoseconds(*rhs)) {
2858 Some(val) => Ok(Value::date(val, span)),
2859 _ => Err(ShellError::OperatorOverflow {
2860 msg: "subtraction operation overflowed".into(),
2861 span,
2862 help: None,
2863 }),
2864 }
2865 }
2866 (Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
2867 checked_duration_operation(*lhs, *rhs, i64::checked_sub, span)
2868 }
2869 (Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
2870 if let Some(val) = *lhs - *rhs {
2871 Ok(Value::filesize(val, span))
2872 } else {
2873 Err(ShellError::OperatorOverflow {
2874 msg: "add operation overflowed".into(),
2875 span,
2876 help: None,
2877 })
2878 }
2879 }
2880 (Value::Custom { val: lhs, .. }, rhs) => {
2881 lhs.operation(self.span(), Operator::Math(Math::Subtract), op, rhs)
2882 }
2883 _ => Err(operator_type_error(
2884 Operator::Math(Math::Subtract),
2885 op,
2886 self,
2887 rhs,
2888 |val| {
2889 matches!(
2890 val,
2891 Value::Int { .. }
2892 | Value::Float { .. }
2893 | Value::Date { .. }
2894 | Value::Duration { .. }
2895 | Value::Filesize { .. },
2896 )
2897 },
2898 )),
2899 }
2900 }
2901
2902 pub fn mul(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
2903 match (self, rhs) {
2904 (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2905 if let Some(val) = lhs.checked_mul(*rhs) {
2906 Ok(Value::int(val, span))
2907 } else {
2908 Err(ShellError::OperatorOverflow {
2909 msg: "multiply operation overflowed".into(),
2910 span,
2911 help: Some("Consider using floating point values for increased range by promoting operand with 'into float'. Note: float has reduced precision!".into()),
2912 })
2913 }
2914 }
2915 (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2916 Ok(Value::float(*lhs as f64 * *rhs, span))
2917 }
2918 (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2919 Ok(Value::float(*lhs * *rhs as f64, span))
2920 }
2921 (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2922 Ok(Value::float(lhs * rhs, span))
2923 }
2924 (Value::Int { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
2925 if let Some(val) = *lhs * *rhs {
2926 Ok(Value::filesize(val, span))
2927 } else {
2928 Err(ShellError::OperatorOverflow {
2929 msg: "multiply operation overflowed".into(),
2930 span,
2931 help: None,
2932 })
2933 }
2934 }
2935 (Value::Filesize { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2936 if let Some(val) = *lhs * *rhs {
2937 Ok(Value::filesize(val, span))
2938 } else {
2939 Err(ShellError::OperatorOverflow {
2940 msg: "multiply operation overflowed".into(),
2941 span,
2942 help: None,
2943 })
2944 }
2945 }
2946 (Value::Float { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
2947 if let Some(val) = *lhs * *rhs {
2948 Ok(Value::filesize(val, span))
2949 } else {
2950 Err(ShellError::OperatorOverflow {
2951 msg: "multiply operation overflowed".into(),
2952 span,
2953 help: None,
2954 })
2955 }
2956 }
2957 (Value::Filesize { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2958 if let Some(val) = *lhs * *rhs {
2959 Ok(Value::filesize(val, span))
2960 } else {
2961 Err(ShellError::OperatorOverflow {
2962 msg: "multiply operation overflowed".into(),
2963 span,
2964 help: None,
2965 })
2966 }
2967 }
2968 (Value::Int { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
2969 checked_duration_operation(*lhs, *rhs, i64::checked_mul, span)
2970 }
2971 (Value::Duration { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2972 checked_duration_operation(*lhs, *rhs, i64::checked_mul, span)
2973 }
2974 (Value::Duration { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2975 Ok(Value::duration((*lhs as f64 * *rhs) as i64, span))
2976 }
2977 (Value::Float { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
2978 Ok(Value::duration((*lhs * *rhs as f64) as i64, span))
2979 }
2980 (Value::Custom { val: lhs, .. }, rhs) => {
2981 lhs.operation(self.span(), Operator::Math(Math::Multiply), op, rhs)
2982 }
2983 _ => Err(operator_type_error(
2984 Operator::Math(Math::Multiply),
2985 op,
2986 self,
2987 rhs,
2988 |val| {
2989 matches!(
2990 val,
2991 Value::Int { .. }
2992 | Value::Float { .. }
2993 | Value::Duration { .. }
2994 | Value::Filesize { .. },
2995 )
2996 },
2997 )),
2998 }
2999 }
3000
3001 pub fn div(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3002 match (self, rhs) {
3003 (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3004 if *rhs == 0 {
3005 Err(ShellError::DivisionByZero { span: op })
3006 } else {
3007 Ok(Value::float(*lhs as f64 / *rhs as f64, span))
3008 }
3009 }
3010 (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3011 if *rhs != 0.0 {
3012 Ok(Value::float(*lhs as f64 / *rhs, span))
3013 } else {
3014 Err(ShellError::DivisionByZero { span: op })
3015 }
3016 }
3017 (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3018 if *rhs != 0 {
3019 Ok(Value::float(*lhs / *rhs as f64, span))
3020 } else {
3021 Err(ShellError::DivisionByZero { span: op })
3022 }
3023 }
3024 (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3025 if *rhs != 0.0 {
3026 Ok(Value::float(lhs / rhs, span))
3027 } else {
3028 Err(ShellError::DivisionByZero { span: op })
3029 }
3030 }
3031 (Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
3032 if *rhs == Filesize::ZERO {
3033 Err(ShellError::DivisionByZero { span: op })
3034 } else {
3035 Ok(Value::float(lhs.get() as f64 / rhs.get() as f64, span))
3036 }
3037 }
3038 (Value::Filesize { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3039 if let Some(val) = lhs.get().checked_div(*rhs) {
3040 Ok(Value::filesize(val, span))
3041 } else if *rhs == 0 {
3042 Err(ShellError::DivisionByZero { span: op })
3043 } else {
3044 Err(ShellError::OperatorOverflow {
3045 msg: "division operation overflowed".into(),
3046 span,
3047 help: None,
3048 })
3049 }
3050 }
3051 (Value::Filesize { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3052 if *rhs != 0.0 {
3053 if let Ok(val) = Filesize::try_from(lhs.get() as f64 / rhs) {
3054 Ok(Value::filesize(val, span))
3055 } else {
3056 Err(ShellError::OperatorOverflow {
3057 msg: "division operation overflowed".into(),
3058 span,
3059 help: None,
3060 })
3061 }
3062 } else {
3063 Err(ShellError::DivisionByZero { span: op })
3064 }
3065 }
3066 (Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
3067 if *rhs == 0 {
3068 Err(ShellError::DivisionByZero { span: op })
3069 } else {
3070 Ok(Value::float(*lhs as f64 / *rhs as f64, span))
3071 }
3072 }
3073 (Value::Duration { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3074 if let Some(val) = lhs.checked_div(*rhs) {
3075 Ok(Value::duration(val, span))
3076 } else if *rhs == 0 {
3077 Err(ShellError::DivisionByZero { span: op })
3078 } else {
3079 Err(ShellError::OperatorOverflow {
3080 msg: "division operation overflowed".into(),
3081 span,
3082 help: None,
3083 })
3084 }
3085 }
3086 (Value::Duration { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3087 if *rhs != 0.0 {
3088 let val = *lhs as f64 / rhs;
3089 if i64::MIN as f64 <= val && val <= i64::MAX as f64 {
3090 Ok(Value::duration(val as i64, span))
3091 } else {
3092 Err(ShellError::OperatorOverflow {
3093 msg: "division operation overflowed".into(),
3094 span,
3095 help: None,
3096 })
3097 }
3098 } else {
3099 Err(ShellError::DivisionByZero { span: op })
3100 }
3101 }
3102 (Value::Custom { val: lhs, .. }, rhs) => {
3103 lhs.operation(self.span(), Operator::Math(Math::Divide), op, rhs)
3104 }
3105 _ => Err(operator_type_error(
3106 Operator::Math(Math::Divide),
3107 op,
3108 self,
3109 rhs,
3110 |val| {
3111 matches!(
3112 val,
3113 Value::Int { .. }
3114 | Value::Float { .. }
3115 | Value::Duration { .. }
3116 | Value::Filesize { .. },
3117 )
3118 },
3119 )),
3120 }
3121 }
3122
3123 pub fn floor_div(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3124 fn checked_div_floor_i64(dividend: i64, divisor: i64) -> Option<i64> {
3126 let quotient = dividend.checked_div(divisor)?;
3127 let remainder = dividend.checked_rem(divisor)?;
3128 if (remainder > 0 && divisor < 0) || (remainder < 0 && divisor > 0) {
3129 Some(quotient - 1)
3135 } else {
3136 Some(quotient)
3137 }
3138 }
3139
3140 fn checked_div_floor_f64(dividend: f64, divisor: f64) -> Option<f64> {
3141 if divisor == 0.0 {
3142 None
3143 } else {
3144 Some((dividend / divisor).floor())
3145 }
3146 }
3147
3148 match (self, rhs) {
3149 (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3150 if let Some(val) = checked_div_floor_i64(*lhs, *rhs) {
3151 Ok(Value::int(val, span))
3152 } else if *rhs == 0 {
3153 Err(ShellError::DivisionByZero { span: op })
3154 } else {
3155 Err(ShellError::OperatorOverflow {
3156 msg: "division operation overflowed".into(),
3157 span,
3158 help: None,
3159 })
3160 }
3161 }
3162 (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3163 if let Some(val) = checked_div_floor_f64(*lhs as f64, *rhs) {
3164 Ok(Value::float(val, span))
3165 } else {
3166 Err(ShellError::DivisionByZero { span: op })
3167 }
3168 }
3169 (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3170 if let Some(val) = checked_div_floor_f64(*lhs, *rhs as f64) {
3171 Ok(Value::float(val, span))
3172 } else {
3173 Err(ShellError::DivisionByZero { span: op })
3174 }
3175 }
3176 (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3177 if let Some(val) = checked_div_floor_f64(*lhs, *rhs) {
3178 Ok(Value::float(val, span))
3179 } else {
3180 Err(ShellError::DivisionByZero { span: op })
3181 }
3182 }
3183 (Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
3184 if let Some(val) = checked_div_floor_i64(lhs.get(), rhs.get()) {
3185 Ok(Value::int(val, span))
3186 } else if *rhs == Filesize::ZERO {
3187 Err(ShellError::DivisionByZero { span: op })
3188 } else {
3189 Err(ShellError::OperatorOverflow {
3190 msg: "division operation overflowed".into(),
3191 span,
3192 help: None,
3193 })
3194 }
3195 }
3196 (Value::Filesize { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3197 if let Some(val) = checked_div_floor_i64(lhs.get(), *rhs) {
3198 Ok(Value::filesize(val, span))
3199 } else if *rhs == 0 {
3200 Err(ShellError::DivisionByZero { span: op })
3201 } else {
3202 Err(ShellError::OperatorOverflow {
3203 msg: "division operation overflowed".into(),
3204 span,
3205 help: None,
3206 })
3207 }
3208 }
3209 (Value::Filesize { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3210 if let Some(val) = checked_div_floor_f64(lhs.get() as f64, *rhs) {
3211 if let Ok(val) = Filesize::try_from(val) {
3212 Ok(Value::filesize(val, span))
3213 } else {
3214 Err(ShellError::OperatorOverflow {
3215 msg: "division operation overflowed".into(),
3216 span,
3217 help: None,
3218 })
3219 }
3220 } else {
3221 Err(ShellError::DivisionByZero { span: op })
3222 }
3223 }
3224 (Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
3225 if let Some(val) = checked_div_floor_i64(*lhs, *rhs) {
3226 Ok(Value::int(val, span))
3227 } else if *rhs == 0 {
3228 Err(ShellError::DivisionByZero { span: op })
3229 } else {
3230 Err(ShellError::OperatorOverflow {
3231 msg: "division operation overflowed".into(),
3232 span,
3233 help: None,
3234 })
3235 }
3236 }
3237 (Value::Duration { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3238 if let Some(val) = checked_div_floor_i64(*lhs, *rhs) {
3239 Ok(Value::duration(val, span))
3240 } else if *rhs == 0 {
3241 Err(ShellError::DivisionByZero { span: op })
3242 } else {
3243 Err(ShellError::OperatorOverflow {
3244 msg: "division operation overflowed".into(),
3245 span,
3246 help: None,
3247 })
3248 }
3249 }
3250 (Value::Duration { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3251 if let Some(val) = checked_div_floor_f64(*lhs as f64, *rhs) {
3252 if i64::MIN as f64 <= val && val <= i64::MAX as f64 {
3253 Ok(Value::duration(val as i64, span))
3254 } else {
3255 Err(ShellError::OperatorOverflow {
3256 msg: "division operation overflowed".into(),
3257 span,
3258 help: None,
3259 })
3260 }
3261 } else {
3262 Err(ShellError::DivisionByZero { span: op })
3263 }
3264 }
3265 (Value::Custom { val: lhs, .. }, rhs) => {
3266 lhs.operation(self.span(), Operator::Math(Math::FloorDivide), op, rhs)
3267 }
3268 _ => Err(operator_type_error(
3269 Operator::Math(Math::FloorDivide),
3270 op,
3271 self,
3272 rhs,
3273 |val| {
3274 matches!(
3275 val,
3276 Value::Int { .. }
3277 | Value::Float { .. }
3278 | Value::Duration { .. }
3279 | Value::Filesize { .. },
3280 )
3281 },
3282 )),
3283 }
3284 }
3285
3286 pub fn modulo(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3287 fn checked_mod_i64(dividend: i64, divisor: i64) -> Option<i64> {
3289 let remainder = dividend.checked_rem(divisor)?;
3290 if (remainder > 0 && divisor < 0) || (remainder < 0 && divisor > 0) {
3291 Some(remainder + divisor)
3294 } else {
3295 Some(remainder)
3296 }
3297 }
3298
3299 fn checked_mod_f64(dividend: f64, divisor: f64) -> Option<f64> {
3300 if divisor == 0.0 {
3301 None
3302 } else {
3303 let remainder = dividend % divisor;
3304 if (remainder > 0.0 && divisor < 0.0) || (remainder < 0.0 && divisor > 0.0) {
3305 Some(remainder + divisor)
3306 } else {
3307 Some(remainder)
3308 }
3309 }
3310 }
3311
3312 match (self, rhs) {
3313 (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3314 if let Some(val) = checked_mod_i64(*lhs, *rhs) {
3315 Ok(Value::int(val, span))
3316 } else if *rhs == 0 {
3317 Err(ShellError::DivisionByZero { span: op })
3318 } else {
3319 Err(ShellError::OperatorOverflow {
3320 msg: "modulo operation overflowed".into(),
3321 span,
3322 help: None,
3323 })
3324 }
3325 }
3326 (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3327 if let Some(val) = checked_mod_f64(*lhs as f64, *rhs) {
3328 Ok(Value::float(val, span))
3329 } else {
3330 Err(ShellError::DivisionByZero { span: op })
3331 }
3332 }
3333 (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3334 if let Some(val) = checked_mod_f64(*lhs, *rhs as f64) {
3335 Ok(Value::float(val, span))
3336 } else {
3337 Err(ShellError::DivisionByZero { span: op })
3338 }
3339 }
3340 (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3341 if let Some(val) = checked_mod_f64(*lhs, *rhs) {
3342 Ok(Value::float(val, span))
3343 } else {
3344 Err(ShellError::DivisionByZero { span: op })
3345 }
3346 }
3347 (Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
3348 if let Some(val) = checked_mod_i64(lhs.get(), rhs.get()) {
3349 Ok(Value::filesize(val, span))
3350 } else if *rhs == Filesize::ZERO {
3351 Err(ShellError::DivisionByZero { span: op })
3352 } else {
3353 Err(ShellError::OperatorOverflow {
3354 msg: "modulo operation overflowed".into(),
3355 span,
3356 help: None,
3357 })
3358 }
3359 }
3360 (Value::Filesize { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3361 if let Some(val) = checked_mod_i64(lhs.get(), *rhs) {
3362 Ok(Value::filesize(val, span))
3363 } else if *rhs == 0 {
3364 Err(ShellError::DivisionByZero { span: op })
3365 } else {
3366 Err(ShellError::OperatorOverflow {
3367 msg: "modulo operation overflowed".into(),
3368 span,
3369 help: None,
3370 })
3371 }
3372 }
3373 (Value::Filesize { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3374 if let Some(val) = checked_mod_f64(lhs.get() as f64, *rhs) {
3375 if let Ok(val) = Filesize::try_from(val) {
3376 Ok(Value::filesize(val, span))
3377 } else {
3378 Err(ShellError::OperatorOverflow {
3379 msg: "modulo operation overflowed".into(),
3380 span,
3381 help: None,
3382 })
3383 }
3384 } else {
3385 Err(ShellError::DivisionByZero { span: op })
3386 }
3387 }
3388 (Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
3389 if let Some(val) = checked_mod_i64(*lhs, *rhs) {
3390 Ok(Value::duration(val, span))
3391 } else if *rhs == 0 {
3392 Err(ShellError::DivisionByZero { span: op })
3393 } else {
3394 Err(ShellError::OperatorOverflow {
3395 msg: "division operation overflowed".into(),
3396 span,
3397 help: None,
3398 })
3399 }
3400 }
3401 (Value::Duration { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3402 if let Some(val) = checked_mod_i64(*lhs, *rhs) {
3403 Ok(Value::duration(val, span))
3404 } else if *rhs == 0 {
3405 Err(ShellError::DivisionByZero { span: op })
3406 } else {
3407 Err(ShellError::OperatorOverflow {
3408 msg: "division operation overflowed".into(),
3409 span,
3410 help: None,
3411 })
3412 }
3413 }
3414 (Value::Duration { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3415 if let Some(val) = checked_mod_f64(*lhs as f64, *rhs) {
3416 if i64::MIN as f64 <= val && val <= i64::MAX as f64 {
3417 Ok(Value::duration(val as i64, span))
3418 } else {
3419 Err(ShellError::OperatorOverflow {
3420 msg: "division operation overflowed".into(),
3421 span,
3422 help: None,
3423 })
3424 }
3425 } else {
3426 Err(ShellError::DivisionByZero { span: op })
3427 }
3428 }
3429 (Value::Custom { val: lhs, .. }, rhs) => {
3430 lhs.operation(span, Operator::Math(Math::Modulo), op, rhs)
3431 }
3432 _ => Err(operator_type_error(
3433 Operator::Math(Math::Modulo),
3434 op,
3435 self,
3436 rhs,
3437 |val| {
3438 matches!(
3439 val,
3440 Value::Int { .. }
3441 | Value::Float { .. }
3442 | Value::Duration { .. }
3443 | Value::Filesize { .. },
3444 )
3445 },
3446 )),
3447 }
3448 }
3449
3450 pub fn pow(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3451 match (self, rhs) {
3452 (Value::Int { val: lhs, .. }, Value::Int { val: rhsv, .. }) => {
3453 if *rhsv < 0 {
3454 return Err(ShellError::IncorrectValue {
3455 msg: "Negative exponent for integer power is unsupported; use floats instead.".into(),
3456 val_span: rhs.span(),
3457 call_span: op,
3458 });
3459 }
3460
3461 if let Some(val) = lhs.checked_pow(*rhsv as u32) {
3462 Ok(Value::int(val, span))
3463 } else {
3464 Err(ShellError::OperatorOverflow {
3465 msg: "pow operation overflowed".into(),
3466 span,
3467 help: Some("Consider using floating point values for increased range by promoting operand with 'into float'. Note: float has reduced precision!".into()),
3468 })
3469 }
3470 }
3471 (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3472 Ok(Value::float((*lhs as f64).powf(*rhs), span))
3473 }
3474 (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3475 Ok(Value::float(lhs.powf(*rhs as f64), span))
3476 }
3477 (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3478 Ok(Value::float(lhs.powf(*rhs), span))
3479 }
3480 (Value::Custom { val: lhs, .. }, rhs) => {
3481 lhs.operation(span, Operator::Math(Math::Pow), op, rhs)
3482 }
3483 _ => Err(operator_type_error(
3484 Operator::Math(Math::Pow),
3485 op,
3486 self,
3487 rhs,
3488 |val| matches!(val, Value::Int { .. } | Value::Float { .. }),
3489 )),
3490 }
3491 }
3492
3493 pub fn concat(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3494 match (self, rhs) {
3495 (Value::List { vals: lhs, .. }, Value::List { vals: rhs, .. }) => {
3496 Ok(Value::list([lhs.as_slice(), rhs.as_slice()].concat(), span))
3497 }
3498 (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
3499 Ok(Value::string([lhs.as_str(), rhs.as_str()].join(""), span))
3500 }
3501 (Value::Binary { val: lhs, .. }, Value::Binary { val: rhs, .. }) => Ok(Value::binary(
3502 [lhs.as_slice(), rhs.as_slice()].concat(),
3503 span,
3504 )),
3505 (Value::Custom { val: lhs, .. }, rhs) => {
3506 lhs.operation(self.span(), Operator::Math(Math::Concatenate), op, rhs)
3507 }
3508 _ => {
3509 let help = if matches!(self, Value::List { .. })
3510 || matches!(rhs, Value::List { .. })
3511 {
3512 Some(
3513 "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`.",
3514 )
3515 } else {
3516 None
3517 };
3518 let is_supported = |val: &Value| {
3519 matches!(
3520 val,
3521 Value::List { .. }
3522 | Value::String { .. }
3523 | Value::Binary { .. }
3524 | Value::Custom { .. }
3525 )
3526 };
3527 Err(match (is_supported(self), is_supported(rhs)) {
3528 (true, true) => ShellError::OperatorIncompatibleTypes {
3529 op: Operator::Math(Math::Concatenate),
3530 lhs: self.get_type(),
3531 rhs: rhs.get_type(),
3532 op_span: op,
3533 lhs_span: self.span(),
3534 rhs_span: rhs.span(),
3535 help,
3536 },
3537 (true, false) => ShellError::OperatorUnsupportedType {
3538 op: Operator::Math(Math::Concatenate),
3539 unsupported: rhs.get_type(),
3540 op_span: op,
3541 unsupported_span: rhs.span(),
3542 help,
3543 },
3544 (false, _) => ShellError::OperatorUnsupportedType {
3545 op: Operator::Math(Math::Concatenate),
3546 unsupported: self.get_type(),
3547 op_span: op,
3548 unsupported_span: self.span(),
3549 help,
3550 },
3551 })
3552 }
3553 }
3554 }
3555
3556 pub fn lt(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3557 if let (Value::Custom { val: lhs, .. }, rhs) = (self, rhs) {
3558 return lhs.operation(
3559 self.span(),
3560 Operator::Comparison(Comparison::LessThan),
3561 op,
3562 rhs,
3563 );
3564 }
3565
3566 if matches!(self, Value::Nothing { .. }) || matches!(rhs, Value::Nothing { .. }) {
3567 return Ok(Value::nothing(span));
3568 }
3569
3570 if !type_compatible(self.get_type(), rhs.get_type()) {
3571 return Err(operator_type_error(
3572 Operator::Comparison(Comparison::LessThan),
3573 op,
3574 self,
3575 rhs,
3576 |val| {
3577 matches!(
3578 val,
3579 Value::Int { .. }
3580 | Value::Float { .. }
3581 | Value::String { .. }
3582 | Value::Filesize { .. }
3583 | Value::Duration { .. }
3584 | Value::Date { .. }
3585 | Value::Bool { .. }
3586 | Value::Nothing { .. }
3587 )
3588 },
3589 ));
3590 }
3591
3592 Ok(Value::bool(
3593 matches!(self.partial_cmp(rhs), Some(Ordering::Less)),
3594 span,
3595 ))
3596 }
3597
3598 pub fn lte(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3599 if let (Value::Custom { val: lhs, .. }, rhs) = (self, rhs) {
3600 return lhs.operation(
3601 self.span(),
3602 Operator::Comparison(Comparison::LessThanOrEqual),
3603 op,
3604 rhs,
3605 );
3606 }
3607
3608 if matches!(self, Value::Nothing { .. }) || matches!(rhs, Value::Nothing { .. }) {
3609 return Ok(Value::nothing(span));
3610 }
3611
3612 if !type_compatible(self.get_type(), rhs.get_type()) {
3613 return Err(operator_type_error(
3614 Operator::Comparison(Comparison::LessThanOrEqual),
3615 op,
3616 self,
3617 rhs,
3618 |val| {
3619 matches!(
3620 val,
3621 Value::Int { .. }
3622 | Value::Float { .. }
3623 | Value::String { .. }
3624 | Value::Filesize { .. }
3625 | Value::Duration { .. }
3626 | Value::Date { .. }
3627 | Value::Bool { .. }
3628 | Value::Nothing { .. }
3629 )
3630 },
3631 ));
3632 }
3633
3634 Ok(Value::bool(
3635 matches!(
3636 self.partial_cmp(rhs),
3637 Some(Ordering::Less | Ordering::Equal)
3638 ),
3639 span,
3640 ))
3641 }
3642
3643 pub fn gt(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3644 if let (Value::Custom { val: lhs, .. }, rhs) = (self, rhs) {
3645 return lhs.operation(
3646 self.span(),
3647 Operator::Comparison(Comparison::GreaterThan),
3648 op,
3649 rhs,
3650 );
3651 }
3652
3653 if matches!(self, Value::Nothing { .. }) || matches!(rhs, Value::Nothing { .. }) {
3654 return Ok(Value::nothing(span));
3655 }
3656
3657 if !type_compatible(self.get_type(), rhs.get_type()) {
3658 return Err(operator_type_error(
3659 Operator::Comparison(Comparison::GreaterThan),
3660 op,
3661 self,
3662 rhs,
3663 |val| {
3664 matches!(
3665 val,
3666 Value::Int { .. }
3667 | Value::Float { .. }
3668 | Value::String { .. }
3669 | Value::Filesize { .. }
3670 | Value::Duration { .. }
3671 | Value::Date { .. }
3672 | Value::Bool { .. }
3673 | Value::Nothing { .. }
3674 )
3675 },
3676 ));
3677 }
3678
3679 Ok(Value::bool(
3680 matches!(self.partial_cmp(rhs), Some(Ordering::Greater)),
3681 span,
3682 ))
3683 }
3684
3685 pub fn gte(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3686 if let (Value::Custom { val: lhs, .. }, rhs) = (self, rhs) {
3687 return lhs.operation(
3688 self.span(),
3689 Operator::Comparison(Comparison::GreaterThanOrEqual),
3690 op,
3691 rhs,
3692 );
3693 }
3694
3695 if matches!(self, Value::Nothing { .. }) || matches!(rhs, Value::Nothing { .. }) {
3696 return Ok(Value::nothing(span));
3697 }
3698
3699 if !type_compatible(self.get_type(), rhs.get_type()) {
3700 return Err(operator_type_error(
3701 Operator::Comparison(Comparison::GreaterThanOrEqual),
3702 op,
3703 self,
3704 rhs,
3705 |val| {
3706 matches!(
3707 val,
3708 Value::Int { .. }
3709 | Value::Float { .. }
3710 | Value::String { .. }
3711 | Value::Filesize { .. }
3712 | Value::Duration { .. }
3713 | Value::Date { .. }
3714 | Value::Bool { .. }
3715 | Value::Nothing { .. }
3716 )
3717 },
3718 ));
3719 }
3720
3721 Ok(Value::bool(
3722 matches!(
3723 self.partial_cmp(rhs),
3724 Some(Ordering::Greater | Ordering::Equal)
3725 ),
3726 span,
3727 ))
3728 }
3729
3730 pub fn eq(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3731 if let (Value::Custom { val: lhs, .. }, rhs) = (self, rhs) {
3732 return lhs.operation(
3733 self.span(),
3734 Operator::Comparison(Comparison::Equal),
3735 op,
3736 rhs,
3737 );
3738 }
3739
3740 Ok(Value::bool(
3741 matches!(self.partial_cmp(rhs), Some(Ordering::Equal)),
3742 span,
3743 ))
3744 }
3745
3746 pub fn ne(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3747 if let (Value::Custom { val: lhs, .. }, rhs) = (self, rhs) {
3748 return lhs.operation(
3749 self.span(),
3750 Operator::Comparison(Comparison::NotEqual),
3751 op,
3752 rhs,
3753 );
3754 }
3755
3756 Ok(Value::bool(
3757 !matches!(self.partial_cmp(rhs), Some(Ordering::Equal)),
3758 span,
3759 ))
3760 }
3761
3762 pub fn r#in(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3763 match (self, rhs) {
3764 (lhs, Value::Range { val: rhs, .. }) => Ok(Value::bool(rhs.contains(lhs), span)),
3765 (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
3766 Ok(Value::bool(rhs.contains(lhs), span))
3767 }
3768 (lhs, Value::List { vals: rhs, .. }) => Ok(Value::bool(rhs.contains(lhs), span)),
3769 (Value::String { val: lhs, .. }, Value::Record { val: rhs, .. }) => {
3770 Ok(Value::bool(rhs.contains(lhs), span))
3771 }
3772 (Value::String { .. } | Value::Int { .. }, Value::CellPath { val: rhs, .. }) => {
3773 let val = rhs.members.iter().any(|member| match (self, member) {
3774 (Value::Int { val: lhs, .. }, PathMember::Int { val: rhs, .. }) => {
3775 *lhs == *rhs as i64
3776 }
3777 (Value::String { val: lhs, .. }, PathMember::String { val: rhs, .. }) => {
3778 lhs == rhs
3779 }
3780 (Value::String { .. }, PathMember::Int { .. })
3781 | (Value::Int { .. }, PathMember::String { .. }) => false,
3782 _ => unreachable!(
3783 "outer match arm ensures `self` is either a `String` or `Int` variant"
3784 ),
3785 });
3786
3787 Ok(Value::bool(val, span))
3788 }
3789 (Value::CellPath { val: lhs, .. }, Value::CellPath { val: rhs, .. }) => {
3790 Ok(Value::bool(
3791 rhs.members
3792 .windows(lhs.members.len())
3793 .any(|member_window| member_window == rhs.members),
3794 span,
3795 ))
3796 }
3797 (Value::Custom { val: lhs, .. }, rhs) => {
3798 lhs.operation(self.span(), Operator::Comparison(Comparison::In), op, rhs)
3799 }
3800 (lhs, rhs) => Err(
3801 if let Value::List { .. }
3802 | Value::Range { .. }
3803 | Value::String { .. }
3804 | Value::Record { .. }
3805 | Value::Custom { .. } = rhs
3806 {
3807 ShellError::OperatorIncompatibleTypes {
3808 op: Operator::Comparison(Comparison::In),
3809 lhs: lhs.get_type(),
3810 rhs: rhs.get_type(),
3811 op_span: op,
3812 lhs_span: lhs.span(),
3813 rhs_span: rhs.span(),
3814 help: None,
3815 }
3816 } else {
3817 ShellError::OperatorUnsupportedType {
3818 op: Operator::Comparison(Comparison::In),
3819 unsupported: rhs.get_type(),
3820 op_span: op,
3821 unsupported_span: rhs.span(),
3822 help: None,
3823 }
3824 },
3825 ),
3826 }
3827 }
3828
3829 pub fn not_in(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3830 match (self, rhs) {
3831 (lhs, Value::Range { val: rhs, .. }) => Ok(Value::bool(!rhs.contains(lhs), span)),
3832 (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
3833 Ok(Value::bool(!rhs.contains(lhs), span))
3834 }
3835 (lhs, Value::List { vals: rhs, .. }) => Ok(Value::bool(!rhs.contains(lhs), span)),
3836 (Value::String { val: lhs, .. }, Value::Record { val: rhs, .. }) => {
3837 Ok(Value::bool(!rhs.contains(lhs), span))
3838 }
3839 (Value::String { .. } | Value::Int { .. }, Value::CellPath { val: rhs, .. }) => {
3840 let val = rhs.members.iter().any(|member| match (self, member) {
3841 (Value::Int { val: lhs, .. }, PathMember::Int { val: rhs, .. }) => {
3842 *lhs != *rhs as i64
3843 }
3844 (Value::String { val: lhs, .. }, PathMember::String { val: rhs, .. }) => {
3845 lhs != rhs
3846 }
3847 (Value::String { .. }, PathMember::Int { .. })
3848 | (Value::Int { .. }, PathMember::String { .. }) => true,
3849 _ => unreachable!(
3850 "outer match arm ensures `self` is either a `String` or `Int` variant"
3851 ),
3852 });
3853
3854 Ok(Value::bool(val, span))
3855 }
3856 (Value::CellPath { val: lhs, .. }, Value::CellPath { val: rhs, .. }) => {
3857 Ok(Value::bool(
3858 rhs.members
3859 .windows(lhs.members.len())
3860 .all(|member_window| member_window != rhs.members),
3861 span,
3862 ))
3863 }
3864 (Value::Custom { val: lhs, .. }, rhs) => lhs.operation(
3865 self.span(),
3866 Operator::Comparison(Comparison::NotIn),
3867 op,
3868 rhs,
3869 ),
3870 (lhs, rhs) => Err(
3871 if let Value::List { .. }
3872 | Value::Range { .. }
3873 | Value::String { .. }
3874 | Value::Record { .. }
3875 | Value::Custom { .. } = rhs
3876 {
3877 ShellError::OperatorIncompatibleTypes {
3878 op: Operator::Comparison(Comparison::NotIn),
3879 lhs: lhs.get_type(),
3880 rhs: rhs.get_type(),
3881 op_span: op,
3882 lhs_span: lhs.span(),
3883 rhs_span: rhs.span(),
3884 help: None,
3885 }
3886 } else {
3887 ShellError::OperatorUnsupportedType {
3888 op: Operator::Comparison(Comparison::NotIn),
3889 unsupported: rhs.get_type(),
3890 op_span: op,
3891 unsupported_span: rhs.span(),
3892 help: None,
3893 }
3894 },
3895 ),
3896 }
3897 }
3898
3899 pub fn has(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3900 rhs.r#in(op, self, span)
3901 }
3902
3903 pub fn not_has(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3904 rhs.r#not_in(op, self, span)
3905 }
3906
3907 pub fn regex_match(
3908 &self,
3909 engine_state: &EngineState,
3910 op: Span,
3911 rhs: &Value,
3912 invert: bool,
3913 span: Span,
3914 ) -> Result<Value, ShellError> {
3915 let rhs_span = rhs.span();
3916 match (self, rhs) {
3917 (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
3918 let is_match = match engine_state.regex_cache.try_lock() {
3919 Ok(mut cache) => {
3920 if let Some(regex) = cache.get(rhs) {
3921 regex.is_match(lhs)
3922 } else {
3923 let regex =
3924 Regex::new(rhs).map_err(|e| ShellError::UnsupportedInput {
3925 msg: format!("{e}"),
3926 input: "value originated from here".into(),
3927 msg_span: span,
3928 input_span: rhs_span,
3929 })?;
3930 let ret = regex.is_match(lhs);
3931 cache.put(rhs.clone(), regex);
3932 ret
3933 }
3934 }
3935 Err(_) => {
3936 let regex = Regex::new(rhs).map_err(|e| ShellError::UnsupportedInput {
3937 msg: format!("{e}"),
3938 input: "value originated from here".into(),
3939 msg_span: span,
3940 input_span: rhs_span,
3941 })?;
3942 regex.is_match(lhs)
3943 }
3944 };
3945
3946 Ok(Value::bool(
3947 if invert {
3948 !is_match.unwrap_or(false)
3949 } else {
3950 is_match.unwrap_or(true)
3951 },
3952 span,
3953 ))
3954 }
3955 (Value::Custom { val: lhs, .. }, rhs) => lhs.operation(
3956 span,
3957 if invert {
3958 Operator::Comparison(Comparison::NotRegexMatch)
3959 } else {
3960 Operator::Comparison(Comparison::RegexMatch)
3961 },
3962 op,
3963 rhs,
3964 ),
3965 _ => Err(operator_type_error(
3966 if invert {
3967 Operator::Comparison(Comparison::NotRegexMatch)
3968 } else {
3969 Operator::Comparison(Comparison::RegexMatch)
3970 },
3971 op,
3972 self,
3973 rhs,
3974 |val| matches!(val, Value::String { .. }),
3975 )),
3976 }
3977 }
3978
3979 pub fn starts_with(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3980 match (self, rhs) {
3981 (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
3982 Ok(Value::bool(lhs.starts_with(rhs), span))
3983 }
3984 (Value::Custom { val: lhs, .. }, rhs) => lhs.operation(
3985 self.span(),
3986 Operator::Comparison(Comparison::StartsWith),
3987 op,
3988 rhs,
3989 ),
3990 _ => Err(operator_type_error(
3991 Operator::Comparison(Comparison::StartsWith),
3992 op,
3993 self,
3994 rhs,
3995 |val| matches!(val, Value::String { .. }),
3996 )),
3997 }
3998 }
3999
4000 pub fn not_starts_with(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
4001 match (self, rhs) {
4002 (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
4003 Ok(Value::bool(!lhs.starts_with(rhs), span))
4004 }
4005 (Value::Custom { val: lhs, .. }, rhs) => lhs.operation(
4006 self.span(),
4007 Operator::Comparison(Comparison::NotStartsWith),
4008 op,
4009 rhs,
4010 ),
4011 _ => Err(operator_type_error(
4012 Operator::Comparison(Comparison::NotStartsWith),
4013 op,
4014 self,
4015 rhs,
4016 |val| matches!(val, Value::String { .. }),
4017 )),
4018 }
4019 }
4020
4021 pub fn ends_with(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
4022 match (self, rhs) {
4023 (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
4024 Ok(Value::bool(lhs.ends_with(rhs), span))
4025 }
4026 (Value::Custom { val: lhs, .. }, rhs) => lhs.operation(
4027 self.span(),
4028 Operator::Comparison(Comparison::EndsWith),
4029 op,
4030 rhs,
4031 ),
4032 _ => Err(operator_type_error(
4033 Operator::Comparison(Comparison::EndsWith),
4034 op,
4035 self,
4036 rhs,
4037 |val| matches!(val, Value::String { .. }),
4038 )),
4039 }
4040 }
4041
4042 pub fn not_ends_with(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
4043 match (self, rhs) {
4044 (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
4045 Ok(Value::bool(!lhs.ends_with(rhs), span))
4046 }
4047 (Value::Custom { val: lhs, .. }, rhs) => lhs.operation(
4048 self.span(),
4049 Operator::Comparison(Comparison::NotEndsWith),
4050 op,
4051 rhs,
4052 ),
4053 _ => Err(operator_type_error(
4054 Operator::Comparison(Comparison::NotEndsWith),
4055 op,
4056 self,
4057 rhs,
4058 |val| matches!(val, Value::String { .. }),
4059 )),
4060 }
4061 }
4062
4063 pub fn bit_or(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
4064 match (self, rhs) {
4065 (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
4066 Ok(Value::int(*lhs | rhs, span))
4067 }
4068 (Value::Custom { val: lhs, .. }, rhs) => {
4069 lhs.operation(span, Operator::Bits(Bits::BitOr), op, rhs)
4070 }
4071 _ => Err(operator_type_error(
4072 Operator::Bits(Bits::BitOr),
4073 op,
4074 self,
4075 rhs,
4076 |val| matches!(val, Value::Int { .. }),
4077 )),
4078 }
4079 }
4080
4081 pub fn bit_xor(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
4082 match (self, rhs) {
4083 (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
4084 Ok(Value::int(*lhs ^ rhs, span))
4085 }
4086 (Value::Custom { val: lhs, .. }, rhs) => {
4087 lhs.operation(span, Operator::Bits(Bits::BitXor), op, rhs)
4088 }
4089 _ => Err(operator_type_error(
4090 Operator::Bits(Bits::BitXor),
4091 op,
4092 self,
4093 rhs,
4094 |val| matches!(val, Value::Int { .. }),
4095 )),
4096 }
4097 }
4098
4099 pub fn bit_and(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
4100 match (self, rhs) {
4101 (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
4102 Ok(Value::int(*lhs & rhs, span))
4103 }
4104 (Value::Custom { val: lhs, .. }, rhs) => {
4105 lhs.operation(span, Operator::Bits(Bits::BitAnd), op, rhs)
4106 }
4107 _ => Err(operator_type_error(
4108 Operator::Bits(Bits::BitAnd),
4109 op,
4110 self,
4111 rhs,
4112 |val| matches!(val, Value::Int { .. }),
4113 )),
4114 }
4115 }
4116
4117 pub fn bit_shl(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
4118 match (self, rhs) {
4119 (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
4120 if let Some(val) = (*rhs).try_into().ok().and_then(|rhs| lhs.checked_shl(rhs)) {
4123 Ok(Value::int(val, span))
4124 } else {
4125 Err(ShellError::OperatorOverflow {
4126 msg: "right operand to bit-shl exceeds available bits in underlying data"
4127 .into(),
4128 span,
4129 help: Some(format!("Limit operand to 0 <= rhs < {}", i64::BITS)),
4130 })
4131 }
4132 }
4133 (Value::Custom { val: lhs, .. }, rhs) => {
4134 lhs.operation(span, Operator::Bits(Bits::ShiftLeft), op, rhs)
4135 }
4136 _ => Err(operator_type_error(
4137 Operator::Bits(Bits::ShiftLeft),
4138 op,
4139 self,
4140 rhs,
4141 |val| matches!(val, Value::Int { .. }),
4142 )),
4143 }
4144 }
4145
4146 pub fn bit_shr(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
4147 match (self, rhs) {
4148 (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
4149 if let Some(val) = (*rhs).try_into().ok().and_then(|rhs| lhs.checked_shr(rhs)) {
4152 Ok(Value::int(val, span))
4153 } else {
4154 Err(ShellError::OperatorOverflow {
4155 msg: "right operand to bit-shr exceeds available bits in underlying data"
4156 .into(),
4157 span,
4158 help: Some(format!("Limit operand to 0 <= rhs < {}", i64::BITS)),
4159 })
4160 }
4161 }
4162 (Value::Custom { val: lhs, .. }, rhs) => {
4163 lhs.operation(span, Operator::Bits(Bits::ShiftRight), op, rhs)
4164 }
4165 _ => Err(operator_type_error(
4166 Operator::Bits(Bits::ShiftRight),
4167 op,
4168 self,
4169 rhs,
4170 |val| matches!(val, Value::Int { .. }),
4171 )),
4172 }
4173 }
4174
4175 pub fn or(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
4176 match (self, rhs) {
4177 (Value::Bool { val: lhs, .. }, Value::Bool { val: rhs, .. }) => {
4178 Ok(Value::bool(*lhs || *rhs, span))
4179 }
4180 (Value::Custom { val: lhs, .. }, rhs) => {
4181 lhs.operation(span, Operator::Boolean(Boolean::Or), op, rhs)
4182 }
4183 _ => Err(operator_type_error(
4184 Operator::Boolean(Boolean::Or),
4185 op,
4186 self,
4187 rhs,
4188 |val| matches!(val, Value::Bool { .. }),
4189 )),
4190 }
4191 }
4192
4193 pub fn xor(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
4194 match (self, rhs) {
4195 (Value::Bool { val: lhs, .. }, Value::Bool { val: rhs, .. }) => {
4196 Ok(Value::bool((*lhs && !*rhs) || (!*lhs && *rhs), span))
4197 }
4198 (Value::Custom { val: lhs, .. }, rhs) => {
4199 lhs.operation(span, Operator::Boolean(Boolean::Xor), op, rhs)
4200 }
4201 _ => Err(operator_type_error(
4202 Operator::Boolean(Boolean::Xor),
4203 op,
4204 self,
4205 rhs,
4206 |val| matches!(val, Value::Bool { .. }),
4207 )),
4208 }
4209 }
4210
4211 pub fn and(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
4212 match (self, rhs) {
4213 (Value::Bool { val: lhs, .. }, Value::Bool { val: rhs, .. }) => {
4214 Ok(Value::bool(*lhs && *rhs, span))
4215 }
4216 (Value::Custom { val: lhs, .. }, rhs) => {
4217 lhs.operation(span, Operator::Boolean(Boolean::And), op, rhs)
4218 }
4219 _ => Err(operator_type_error(
4220 Operator::Boolean(Boolean::And),
4221 op,
4222 self,
4223 rhs,
4224 |val| matches!(val, Value::Bool { .. }),
4225 )),
4226 }
4227 }
4228}
4229
4230fn type_compatible(a: Type, b: Type) -> bool {
4233 if a == b {
4234 return true;
4235 }
4236
4237 matches!((a, b), (Type::Int, Type::Float) | (Type::Float, Type::Int))
4238}
4239
4240fn operator_type_error(
4241 op: Operator,
4242 op_span: Span,
4243 lhs: &Value,
4244 rhs: &Value,
4245 is_supported: fn(&Value) -> bool,
4246) -> ShellError {
4247 let is_supported = |val| is_supported(val) || matches!(val, Value::Custom { .. });
4248 match (is_supported(lhs), is_supported(rhs)) {
4249 (true, true) => ShellError::OperatorIncompatibleTypes {
4250 op,
4251 lhs: lhs.get_type(),
4252 rhs: rhs.get_type(),
4253 op_span,
4254 lhs_span: lhs.span(),
4255 rhs_span: rhs.span(),
4256 help: None,
4257 },
4258 (true, false) => ShellError::OperatorUnsupportedType {
4259 op,
4260 unsupported: rhs.get_type(),
4261 op_span,
4262 unsupported_span: rhs.span(),
4263 help: None,
4264 },
4265 (false, _) => ShellError::OperatorUnsupportedType {
4266 op,
4267 unsupported: lhs.get_type(),
4268 op_span,
4269 unsupported_span: lhs.span(),
4270 help: None,
4271 },
4272 }
4273}
4274
4275pub fn human_time_from_now(val: &DateTime<FixedOffset>) -> HumanTime {
4276 let now = Local::now().with_timezone(val.offset());
4277 let delta = *val - now;
4278 match delta.num_nanoseconds() {
4279 Some(num_nanoseconds) => {
4280 let delta_seconds = num_nanoseconds as f64 / 1_000_000_000.0;
4281 let delta_seconds_rounded = delta_seconds.round() as i64;
4282 HumanTime::from(Duration::seconds(delta_seconds_rounded))
4283 }
4284 None => {
4285 let delta_years = val.year() - now.year();
4288 HumanTime::from(Duration::days(delta_years as i64 * 365))
4289 }
4290 }
4291}
4292
4293#[cfg(test)]
4294mod tests {
4295 use super::{Record, Value};
4296 use crate::record;
4297
4298 mod at_cell_path {
4299 use crate::casing::Casing;
4300
4301 use crate::{IntoValue, Span};
4302
4303 use super::super::PathMember;
4304 use super::*;
4305
4306 #[test]
4307 fn test_record_with_data_at_cell_path() {
4308 let value_to_insert = Value::test_string("value");
4309 let span = Span::test_data();
4310 assert_eq!(
4311 Value::with_data_at_cell_path(
4312 &[
4313 PathMember::test_string("a".to_string(), false, Casing::Sensitive),
4314 PathMember::test_string("b".to_string(), false, Casing::Sensitive),
4315 PathMember::test_string("c".to_string(), false, Casing::Sensitive),
4316 PathMember::test_string("d".to_string(), false, Casing::Sensitive),
4317 ],
4318 value_to_insert,
4319 ),
4320 Ok(record!(
4322 "a" => record!(
4323 "b" => record!(
4324 "c" => record!(
4325 "d" => Value::test_string("value")
4326 ).into_value(span)
4327 ).into_value(span)
4328 ).into_value(span)
4329 )
4330 .into_value(span))
4331 );
4332 }
4333
4334 #[test]
4335 fn test_lists_with_data_at_cell_path() {
4336 let value_to_insert = Value::test_string("value");
4337 assert_eq!(
4338 Value::with_data_at_cell_path(
4339 &[
4340 PathMember::test_int(0, false),
4341 PathMember::test_int(0, false),
4342 PathMember::test_int(0, false),
4343 PathMember::test_int(0, false),
4344 ],
4345 value_to_insert.clone(),
4346 ),
4347 Ok(Value::test_list(vec![Value::test_list(vec![
4349 Value::test_list(vec![Value::test_list(vec![value_to_insert])])
4350 ])]))
4351 );
4352 }
4353 #[test]
4354 fn test_mixed_with_data_at_cell_path() {
4355 let value_to_insert = Value::test_string("value");
4356 let span = Span::test_data();
4357 assert_eq!(
4358 Value::with_data_at_cell_path(
4359 &[
4360 PathMember::test_string("a".to_string(), false, Casing::Sensitive),
4361 PathMember::test_int(0, false),
4362 PathMember::test_string("b".to_string(), false, Casing::Sensitive),
4363 PathMember::test_int(0, false),
4364 PathMember::test_string("c".to_string(), false, Casing::Sensitive),
4365 PathMember::test_int(0, false),
4366 PathMember::test_string("d".to_string(), false, Casing::Sensitive),
4367 PathMember::test_int(0, false),
4368 ],
4369 value_to_insert.clone(),
4370 ),
4371 Ok(record!(
4373 "a" => Value::test_list(vec![record!(
4374 "b" => Value::test_list(vec![record!(
4375 "c" => Value::test_list(vec![record!(
4376 "d" => Value::test_list(vec![value_to_insert])
4377 ).into_value(span)])
4378 ).into_value(span)])
4379 ).into_value(span)])
4380 )
4381 .into_value(span))
4382 );
4383 }
4384
4385 #[test]
4386 fn test_nested_upsert_data_at_cell_path() {
4387 let span = Span::test_data();
4388 let mut base_value = record!(
4389 "a" => Value::test_list(vec![])
4390 )
4391 .into_value(span);
4392
4393 let value_to_insert = Value::test_string("value");
4394 let res = base_value.upsert_data_at_cell_path(
4395 &[
4396 PathMember::test_string("a".to_string(), false, Casing::Sensitive),
4397 PathMember::test_int(0, false),
4398 PathMember::test_string("b".to_string(), false, Casing::Sensitive),
4399 PathMember::test_int(0, false),
4400 ],
4401 value_to_insert.clone(),
4402 );
4403 assert_eq!(res, Ok(()));
4404 assert_eq!(
4405 base_value,
4406 record!(
4408 "a" => Value::test_list(vec![
4409 record!(
4410 "b" => Value::test_list(vec![value_to_insert])
4411 )
4412 .into_value(span)
4413 ])
4414 )
4415 .into_value(span)
4416 );
4417 }
4418
4419 #[test]
4420 fn test_nested_insert_data_at_cell_path() {
4421 let span = Span::test_data();
4422 let mut base_value = record!(
4423 "a" => Value::test_list(vec![])
4424 )
4425 .into_value(span);
4426
4427 let value_to_insert = Value::test_string("value");
4428 let res = base_value.insert_data_at_cell_path(
4429 &[
4430 PathMember::test_string("a".to_string(), false, Casing::Sensitive),
4431 PathMember::test_int(0, false),
4432 PathMember::test_string("b".to_string(), false, Casing::Sensitive),
4433 PathMember::test_int(0, false),
4434 ],
4435 value_to_insert.clone(),
4436 span,
4437 );
4438 assert_eq!(res, Ok(()));
4439 assert_eq!(
4440 base_value,
4441 record!(
4443 "a" => Value::test_list(vec![
4444 record!(
4445 "b" => Value::test_list(vec![value_to_insert])
4446 )
4447 .into_value(span)
4448 ])
4449 )
4450 .into_value(span)
4451 );
4452 }
4453 }
4454
4455 mod is_empty {
4456 use super::*;
4457
4458 #[test]
4459 fn test_string() {
4460 let value = Value::test_string("");
4461 assert!(value.is_empty());
4462 }
4463
4464 #[test]
4465 fn test_list() {
4466 let list_with_no_values = Value::test_list(vec![]);
4467 let list_with_one_empty_string = Value::test_list(vec![Value::test_string("")]);
4468
4469 assert!(list_with_no_values.is_empty());
4470 assert!(!list_with_one_empty_string.is_empty());
4471 }
4472
4473 #[test]
4474 fn test_record() {
4475 let no_columns_nor_cell_values = Value::test_record(Record::new());
4476
4477 let one_column_and_one_cell_value_with_empty_strings = Value::test_record(record! {
4478 "" => Value::test_string(""),
4479 });
4480
4481 let one_column_with_a_string_and_one_cell_value_with_empty_string =
4482 Value::test_record(record! {
4483 "column" => Value::test_string(""),
4484 });
4485
4486 let one_column_with_empty_string_and_one_value_with_a_string =
4487 Value::test_record(record! {
4488 "" => Value::test_string("text"),
4489 });
4490
4491 assert!(no_columns_nor_cell_values.is_empty());
4492 assert!(!one_column_and_one_cell_value_with_empty_strings.is_empty());
4493 assert!(!one_column_with_a_string_and_one_cell_value_with_empty_string.is_empty());
4494 assert!(!one_column_with_empty_string_and_one_value_with_a_string.is_empty());
4495 }
4496 }
4497
4498 mod get_type {
4499 use crate::Type;
4500
4501 use super::*;
4502
4503 #[test]
4504 fn test_list() {
4505 let list_of_ints = Value::test_list(vec![Value::test_int(0)]);
4506 let list_of_floats = Value::test_list(vec![Value::test_float(0.0)]);
4507 let list_of_ints_and_floats =
4508 Value::test_list(vec![Value::test_int(0), Value::test_float(0.0)]);
4509 let list_of_ints_and_floats_and_bools = Value::test_list(vec![
4510 Value::test_int(0),
4511 Value::test_float(0.0),
4512 Value::test_bool(false),
4513 ]);
4514 assert_eq!(list_of_ints.get_type(), Type::List(Box::new(Type::Int)));
4515 assert_eq!(list_of_floats.get_type(), Type::List(Box::new(Type::Float)));
4516 assert_eq!(
4517 list_of_ints_and_floats_and_bools.get_type(),
4518 Type::List(Box::new(Type::OneOf(
4519 vec![Type::Number, Type::Bool].into_boxed_slice()
4520 )))
4521 );
4522 assert_eq!(
4523 list_of_ints_and_floats.get_type(),
4524 Type::List(Box::new(Type::Number))
4525 );
4526 }
4527 }
4528
4529 mod is_subtype {
4530 use crate::Type;
4531
4532 use super::*;
4533
4534 fn assert_subtype_equivalent(value: &Value, ty: &Type) {
4535 assert_eq!(value.is_subtype_of(ty), value.get_type().is_subtype_of(ty));
4536 }
4537
4538 #[test]
4539 fn test_list() {
4540 let ty_int_list = Type::list(Type::Int);
4541 let ty_str_list = Type::list(Type::String);
4542 let ty_any_list = Type::list(Type::Any);
4543 let ty_list_list_int = Type::list(Type::list(Type::Int));
4544
4545 let list = Value::test_list(vec![
4546 Value::test_int(1),
4547 Value::test_int(2),
4548 Value::test_int(3),
4549 ]);
4550
4551 assert_subtype_equivalent(&list, &ty_int_list);
4552 assert_subtype_equivalent(&list, &ty_str_list);
4553 assert_subtype_equivalent(&list, &ty_any_list);
4554
4555 let list = Value::test_list(vec![
4556 Value::test_int(1),
4557 Value::test_string("hi"),
4558 Value::test_int(3),
4559 ]);
4560
4561 assert_subtype_equivalent(&list, &ty_int_list);
4562 assert_subtype_equivalent(&list, &ty_str_list);
4563 assert_subtype_equivalent(&list, &ty_any_list);
4564
4565 let list = Value::test_list(vec![Value::test_list(vec![Value::test_int(1)])]);
4566
4567 assert_subtype_equivalent(&list, &ty_list_list_int);
4568
4569 let ty_table = Type::Table(Box::new([
4571 ("a".into(), Type::Int),
4572 ("b".into(), Type::Int),
4573 ("c".into(), Type::Int),
4574 ]));
4575 let empty = Value::test_list(vec![]);
4576
4577 assert_subtype_equivalent(&empty, &ty_any_list);
4578 assert!(empty.is_subtype_of(&ty_int_list));
4579 assert!(empty.is_subtype_of(&ty_table));
4580 }
4581
4582 #[test]
4583 fn test_record() {
4584 let ty_abc = Type::Record(Box::new([
4585 ("a".into(), Type::Int),
4586 ("b".into(), Type::Int),
4587 ("c".into(), Type::Int),
4588 ]));
4589 let ty_ab = Type::Record(Box::new([("a".into(), Type::Int), ("b".into(), Type::Int)]));
4590 let ty_inner = Type::Record(Box::new([("inner".into(), ty_abc.clone())]));
4591
4592 let record_abc = Value::test_record(record! {
4593 "a" => Value::test_int(1),
4594 "b" => Value::test_int(2),
4595 "c" => Value::test_int(3),
4596 });
4597 let record_ab = Value::test_record(record! {
4598 "a" => Value::test_int(1),
4599 "b" => Value::test_int(2),
4600 });
4601
4602 assert_subtype_equivalent(&record_abc, &ty_abc);
4603 assert_subtype_equivalent(&record_abc, &ty_ab);
4604 assert_subtype_equivalent(&record_ab, &ty_abc);
4605 assert_subtype_equivalent(&record_ab, &ty_ab);
4606
4607 let record_inner = Value::test_record(record! {
4608 "inner" => record_abc
4609 });
4610 assert_subtype_equivalent(&record_inner, &ty_inner);
4611 }
4612
4613 #[test]
4614 fn test_table() {
4615 let ty_abc = Type::Table(Box::new([
4616 ("a".into(), Type::Int),
4617 ("b".into(), Type::Int),
4618 ("c".into(), Type::Int),
4619 ]));
4620 let ty_ab = Type::Table(Box::new([("a".into(), Type::Int), ("b".into(), Type::Int)]));
4621 let ty_list_any = Type::list(Type::Any);
4622
4623 let record_abc = Value::test_record(record! {
4624 "a" => Value::test_int(1),
4625 "b" => Value::test_int(2),
4626 "c" => Value::test_int(3),
4627 });
4628 let record_ab = Value::test_record(record! {
4629 "a" => Value::test_int(1),
4630 "b" => Value::test_int(2),
4631 });
4632
4633 let table_abc = Value::test_list(vec![record_abc.clone(), record_abc.clone()]);
4634 let table_ab = Value::test_list(vec![record_ab.clone(), record_ab.clone()]);
4635
4636 assert_subtype_equivalent(&table_abc, &ty_abc);
4637 assert_subtype_equivalent(&table_abc, &ty_ab);
4638 assert_subtype_equivalent(&table_ab, &ty_abc);
4639 assert_subtype_equivalent(&table_ab, &ty_ab);
4640 assert_subtype_equivalent(&table_abc, &ty_list_any);
4641
4642 let table_mixed = Value::test_list(vec![record_abc.clone(), record_ab.clone()]);
4643 assert_subtype_equivalent(&table_mixed, &ty_abc);
4644 assert!(table_mixed.is_subtype_of(&ty_ab));
4645
4646 let ty_a = Type::Table(Box::new([("a".into(), Type::Any)]));
4647 let table_mixed_types = Value::test_list(vec![
4648 Value::test_record(record! {
4649 "a" => Value::test_int(1),
4650 }),
4651 Value::test_record(record! {
4652 "a" => Value::test_string("a"),
4653 }),
4654 ]);
4655 assert!(table_mixed_types.is_subtype_of(&ty_a));
4656 }
4657 }
4658
4659 mod into_string {
4660 use chrono::{DateTime, FixedOffset};
4661
4662 use super::*;
4663
4664 #[test]
4665 fn test_datetime() {
4666 let date = DateTime::from_timestamp_millis(-123456789)
4667 .unwrap()
4668 .with_timezone(&FixedOffset::east_opt(0).unwrap());
4669
4670 let string = Value::test_date(date).to_expanded_string("", &Default::default());
4671
4672 let formatted = string.split('(').next().unwrap();
4675 assert_eq!("Tue, 30 Dec 1969 13:42:23 +0000 ", formatted);
4676 }
4677
4678 #[test]
4679 fn test_negative_year_datetime() {
4680 let date = DateTime::from_timestamp_millis(-72135596800000)
4681 .unwrap()
4682 .with_timezone(&FixedOffset::east_opt(0).unwrap());
4683
4684 let string = Value::test_date(date).to_expanded_string("", &Default::default());
4685
4686 let formatted = string.split(' ').next().unwrap();
4689 assert_eq!("-0316-02-11T06:13:20+00:00", formatted);
4690 }
4691 }
4692
4693 #[test]
4694 fn test_env_as_bool() {
4695 assert_eq!(Value::test_bool(false).coerce_bool(), Ok(false));
4697 assert_eq!(Value::test_int(0).coerce_bool(), Ok(false));
4698 assert_eq!(Value::test_float(0.0).coerce_bool(), Ok(false));
4699 assert_eq!(Value::test_string("").coerce_bool(), Ok(false));
4700 assert_eq!(Value::test_string("0").coerce_bool(), Ok(false));
4701 assert_eq!(Value::test_nothing().coerce_bool(), Ok(false));
4702
4703 assert_eq!(Value::test_bool(true).coerce_bool(), Ok(true));
4705 assert_eq!(Value::test_int(1).coerce_bool(), Ok(true));
4706 assert_eq!(Value::test_float(1.0).coerce_bool(), Ok(true));
4707 assert_eq!(Value::test_string("1").coerce_bool(), Ok(true));
4708
4709 assert_eq!(Value::test_int(42).coerce_bool(), Ok(true));
4711 assert_eq!(Value::test_float(0.5).coerce_bool(), Ok(true));
4712 assert_eq!(Value::test_string("not zero").coerce_bool(), Ok(true));
4713
4714 assert!(Value::test_record(Record::default()).coerce_bool().is_err());
4716 assert!(
4717 Value::test_list(vec![Value::test_int(1)])
4718 .coerce_bool()
4719 .is_err()
4720 );
4721 assert!(
4722 Value::test_date(
4723 chrono::DateTime::parse_from_rfc3339("2024-01-01T12:00:00+00:00").unwrap(),
4724 )
4725 .coerce_bool()
4726 .is_err()
4727 );
4728 assert!(Value::test_glob("*.rs").coerce_bool().is_err());
4729 assert!(Value::test_binary(vec![1, 2, 3]).coerce_bool().is_err());
4730 assert!(Value::test_duration(3600).coerce_bool().is_err());
4731 }
4732
4733 mod memory_size {
4734 use super::*;
4735
4736 #[test]
4737 fn test_primitive_sizes() {
4738 let base_size = std::mem::size_of::<Value>();
4740
4741 assert_eq!(Value::test_bool(true).memory_size(), base_size);
4742 assert_eq!(Value::test_int(42).memory_size(), base_size);
4743 assert_eq!(Value::test_float(1.5).memory_size(), base_size);
4744 assert_eq!(Value::test_nothing().memory_size(), base_size);
4745 }
4746
4747 #[test]
4748 fn test_string_size() {
4749 let s = "hello world";
4750 let val = Value::test_string(s);
4751 let base_size = std::mem::size_of::<Value>();
4752 let string_val = String::from(s);
4754 let expected = base_size + string_val.capacity();
4755 assert_eq!(val.memory_size(), expected);
4756 }
4757
4758 #[test]
4759 fn test_binary_size() {
4760 let data = vec![1, 2, 3, 4, 5];
4761 let val = Value::test_binary(data.clone());
4762 let base_size = std::mem::size_of::<Value>();
4763 let expected = base_size + data.capacity();
4764 assert_eq!(val.memory_size(), expected);
4765 }
4766
4767 #[test]
4768 fn test_list_size() {
4769 let list = Value::test_list(vec![
4770 Value::test_int(1),
4771 Value::test_int(2),
4772 Value::test_int(3),
4773 ]);
4774
4775 let base_size = std::mem::size_of::<Value>();
4776 let element_size = std::mem::size_of::<Value>();
4777 let expected = base_size + 3 * element_size;
4779 assert_eq!(list.memory_size(), expected);
4780 }
4781
4782 #[test]
4783 fn test_record_size() {
4784 let record = Value::test_record(record! {
4785 "a" => Value::test_int(1),
4786 "b" => Value::test_string("hello"),
4787 });
4788
4789 let base_size = std::mem::size_of::<Value>();
4790 let record_base_size = std::mem::size_of::<Record>();
4791 let key1_size = String::from("a").capacity();
4792 let key2_size = String::from("b").capacity();
4793 let val1_size = std::mem::size_of::<Value>();
4794 let val2_base_size = std::mem::size_of::<Value>();
4795 let val2_string_size = String::from("hello").capacity();
4796
4797 let expected = base_size
4798 + record_base_size
4799 + key1_size
4800 + key2_size
4801 + val1_size
4802 + (val2_base_size + val2_string_size);
4803 assert_eq!(record.memory_size(), expected);
4804 }
4805
4806 #[test]
4807 fn test_nested_structure_size() {
4808 let inner_record = Value::test_record(record! {
4810 "x" => Value::test_int(10),
4811 "y" => Value::test_string("test"),
4812 });
4813
4814 let list = Value::test_list(vec![inner_record]);
4815
4816 let record_size = list.memory_size();
4817 let base_size = std::mem::size_of::<Value>();
4819 assert!(record_size > base_size);
4820
4821 let simple_list = Value::test_list(vec![Value::test_int(1)]);
4823 assert!(record_size > simple_list.memory_size());
4824 }
4825 }
4826}