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