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;
12use bstr::BStr;
13pub use custom_value::CustomValue;
14pub use duration::*;
15pub use filesize::*;
16pub use from_value::FromValue;
17pub use glob::*;
18pub use into_value::{IntoValue, TryIntoValue};
19pub use nu_utils::MultiLife;
20pub use range::{FloatRange, IntRange, Range};
21pub use record::Record;
22
23use crate::{
24 BlockId, CompareTypes, Config, ShellError, Signals, Span, Type, TypeRelation,
25 ast::{Bits, Boolean, CellPath, Comparison, Math, Operator, PathMember},
26 did_you_mean,
27 engine::{Closure, EngineState},
28};
29use chrono::{DateTime, Datelike, Duration, FixedOffset, Local, Locale, TimeZone};
30use chrono_humanize::HumanTime;
31use fancy_regex::Regex;
32use nu_utils::{ObviousFloat, SharedCow, contains_emoji, get_locale_from_env_vars};
33pub use semver::Version as SemVerVersion;
34use serde::{Deserialize, Serialize};
35use std::{
36 borrow::Cow,
37 cmp::Ordering,
38 fmt::{self, Debug, Display, Write},
39 ops::{Bound, ControlFlow},
40 path::PathBuf,
41};
42
43#[derive(Serialize, Deserialize)]
78pub enum Value {
79 #[non_exhaustive]
80 Bool {
81 val: bool,
82 #[serde(rename = "span")]
85 internal_span: Span,
86 },
87 #[non_exhaustive]
88 Int {
89 val: i64,
90 #[serde(rename = "span")]
93 internal_span: Span,
94 },
95 #[non_exhaustive]
96 Float {
97 val: f64,
98 #[serde(rename = "span")]
101 internal_span: Span,
102 },
103 #[non_exhaustive]
104 String {
105 val: String,
106 #[serde(rename = "span")]
109 internal_span: Span,
110 },
111 #[non_exhaustive]
112 Glob {
113 val: String,
114 no_expand: bool,
115 #[serde(rename = "span")]
118 internal_span: Span,
119 },
120 #[non_exhaustive]
121 Filesize {
122 val: Filesize,
123 #[serde(rename = "span")]
126 internal_span: Span,
127 },
128 #[non_exhaustive]
129 Duration {
130 val: i64,
132 #[serde(rename = "span")]
135 internal_span: Span,
136 },
137 #[non_exhaustive]
138 Date {
139 val: DateTime<FixedOffset>,
140 #[serde(rename = "span")]
143 internal_span: Span,
144 },
145 #[non_exhaustive]
146 Range {
147 val: Box<Range>,
148 #[serde(skip)]
149 signals: Option<Signals>,
150 #[serde(rename = "span")]
153 internal_span: Span,
154 },
155 #[non_exhaustive]
156 Record {
157 val: SharedCow<Record>,
158 #[serde(rename = "span")]
161 internal_span: Span,
162 },
163 #[non_exhaustive]
164 List {
165 vals: Vec<Value>,
166 #[serde(skip)]
167 signals: Option<Signals>,
168 #[serde(rename = "span")]
171 internal_span: Span,
172 },
173 #[non_exhaustive]
174 Closure {
175 val: Box<Closure>,
176 #[serde(rename = "span")]
179 internal_span: Span,
180 },
181 #[non_exhaustive]
182 Error {
183 error: Box<ShellError>,
184 #[serde(rename = "span")]
187 internal_span: Span,
188 },
189 #[non_exhaustive]
190 Binary {
191 val: Vec<u8>,
192 #[serde(rename = "span")]
195 internal_span: Span,
196 },
197 #[non_exhaustive]
198 CellPath {
199 val: CellPath,
200 #[serde(rename = "span")]
203 internal_span: Span,
204 },
205 #[non_exhaustive]
206 Custom {
207 val: Box<dyn CustomValue>,
208 #[serde(rename = "span")]
211 internal_span: Span,
212 },
213 #[non_exhaustive]
214 Nothing {
215 #[serde(rename = "span")]
218 internal_span: Span,
219 },
220}
221
222fn wrap_tuple(name: &str, val: impl Debug) -> impl Debug {
223 fmt::from_fn(move |f| {
224 write!(f, "{name}(")?;
225 val.fmt(f)?;
226 write!(f, ")")
227 })
228}
229
230fn display_as_debug(val: impl Display) -> impl Debug {
231 fmt::from_fn(move |f| val.fmt(f))
232}
233
234impl Debug for Value {
235 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
236 if f.sign_minus() {
237 return match self {
238 Self::Bool { val, internal_span } => f
239 .debug_struct("Bool")
240 .field("val", val)
241 .field("internal_span", internal_span)
242 .finish(),
243 Self::Int { val, internal_span } => f
244 .debug_struct("Int")
245 .field("val", val)
246 .field("internal_span", internal_span)
247 .finish(),
248 Self::Float { val, internal_span } => f
249 .debug_struct("Float")
250 .field("val", val)
251 .field("internal_span", internal_span)
252 .finish(),
253 Self::String { val, internal_span } => f
254 .debug_struct("String")
255 .field("val", val)
256 .field("internal_span", internal_span)
257 .finish(),
258 Self::Glob {
259 val,
260 no_expand,
261 internal_span,
262 } => f
263 .debug_struct("Glob")
264 .field("val", val)
265 .field("no_expand", no_expand)
266 .field("internal_span", internal_span)
267 .finish(),
268 Self::Filesize { val, internal_span } => f
269 .debug_struct("Filesize")
270 .field("val", val)
271 .field("internal_span", internal_span)
272 .finish(),
273 Self::Duration { val, internal_span } => f
274 .debug_struct("Duration")
275 .field("val", val)
276 .field("internal_span", internal_span)
277 .finish(),
278 Self::Date { val, internal_span } => f
279 .debug_struct("Date")
280 .field("val", val)
281 .field("internal_span", internal_span)
282 .finish(),
283 Self::Range {
284 val,
285 signals,
286 internal_span,
287 } => f
288 .debug_struct("Range")
289 .field("val", val)
290 .field("signals", signals)
291 .field("internal_span", internal_span)
292 .finish(),
293 Self::Record { val, internal_span } => f
294 .debug_struct("Record")
295 .field("val", val)
296 .field("internal_span", internal_span)
297 .finish(),
298 Self::List {
299 vals,
300 signals,
301 internal_span,
302 } => f
303 .debug_struct("List")
304 .field("vals", vals)
305 .field("signals", signals)
306 .field("internal_span", internal_span)
307 .finish(),
308 Self::Closure { val, internal_span } => f
309 .debug_struct("Closure")
310 .field("val", val)
311 .field("internal_span", internal_span)
312 .finish(),
313 Self::Error {
314 error,
315 internal_span,
316 } => f
317 .debug_struct("Error")
318 .field("error", error)
319 .field("internal_span", internal_span)
320 .finish(),
321 Self::Binary { val, internal_span } => f
322 .debug_struct("Binary")
323 .field("val", val)
324 .field("internal_span", internal_span)
325 .finish(),
326 Self::CellPath { val, internal_span } => f
327 .debug_struct("CellPath")
328 .field("val", val)
329 .field("internal_span", internal_span)
330 .finish(),
331 Self::Custom { val, internal_span } => f
332 .debug_struct("Custom")
333 .field("val", val)
334 .field("internal_span", internal_span)
335 .finish(),
336 Self::Nothing { internal_span } => f
337 .debug_struct("Nothing")
338 .field("internal_span", internal_span)
339 .finish(),
340 };
341 };
342
343 match self {
344 Value::Bool { val, .. } => wrap_tuple("Bool", val).fmt(f),
345 Value::Int { val, .. } => wrap_tuple("Int", val).fmt(f),
346 Value::Float { val, .. } => wrap_tuple("Float", val).fmt(f),
347 Value::String { val, .. } => wrap_tuple("String", val).fmt(f),
348 Value::Glob { val, no_expand, .. } => wrap_tuple(
349 "Glob",
350 fmt::from_fn(|f| {
351 Debug::fmt(val, f)?;
352 if *no_expand {
353 write!(f, "!")?;
354 }
355 Ok(())
356 }),
357 )
358 .fmt(f),
359 Value::Filesize { val, .. } => wrap_tuple("Filesize", display_as_debug(val)).fmt(f),
360 Value::Duration { val, .. } => wrap_tuple(
361 "Duration",
362 display_as_debug(humantime::Duration::from(std::time::Duration::from_nanos(
363 *val as u64,
364 ))),
365 )
366 .fmt(f),
367 Value::Date { val, .. } => wrap_tuple("Date", val).fmt(f),
368 Value::Range { val, .. } => wrap_tuple("Range", display_as_debug(val)).fmt(f),
369 Value::Record { val, .. } => wrap_tuple("Record", val).fmt(f),
370 Value::List { vals, .. } => wrap_tuple("List", vals).fmt(f),
371 Value::Closure { val, .. } => wrap_tuple("Closure", val.compact_debug()).fmt(f),
372 Value::Error { error, .. } => wrap_tuple("Error", error).fmt(f),
373 Value::Binary { val, .. } => wrap_tuple("Binary", BStr::new(val)).fmt(f),
374 Value::CellPath { val, .. } => wrap_tuple("CellPath", display_as_debug(val)).fmt(f),
375 Value::Custom { val, .. } => wrap_tuple("Custom", val).fmt(f),
376 Value::Nothing { .. } => write!(f, "Nothing"),
377 }
378 }
379}
380
381const _: () = assert!(std::mem::size_of::<Value>() <= 56);
385
386impl Clone for Value {
387 fn clone(&self) -> Self {
388 match self {
389 Value::Bool { val, internal_span } => Value::bool(*val, *internal_span),
390 Value::Int { val, internal_span } => Value::int(*val, *internal_span),
391 Value::Filesize { val, internal_span } => Value::Filesize {
392 val: *val,
393 internal_span: *internal_span,
394 },
395 Value::Duration { val, internal_span } => Value::Duration {
396 val: *val,
397 internal_span: *internal_span,
398 },
399 Value::Date { val, internal_span } => Value::Date {
400 val: *val,
401 internal_span: *internal_span,
402 },
403 Value::Range {
404 val,
405 signals,
406 internal_span,
407 } => Value::Range {
408 val: val.clone(),
409 signals: signals.clone(),
410 internal_span: *internal_span,
411 },
412 Value::Float { val, internal_span } => Value::float(*val, *internal_span),
413 Value::String { val, internal_span } => Value::String {
414 val: val.clone(),
415 internal_span: *internal_span,
416 },
417 Value::Glob {
418 val,
419 no_expand: quoted,
420 internal_span,
421 } => Value::Glob {
422 val: val.clone(),
423 no_expand: *quoted,
424 internal_span: *internal_span,
425 },
426 Value::Record { val, internal_span } => Value::Record {
427 val: val.clone(),
428 internal_span: *internal_span,
429 },
430 Value::List {
431 vals,
432 signals,
433 internal_span,
434 } => Value::List {
435 vals: vals.clone(),
436 signals: signals.clone(),
437 internal_span: *internal_span,
438 },
439 Value::Closure { val, internal_span } => Value::Closure {
440 val: val.clone(),
441 internal_span: *internal_span,
442 },
443 Value::Nothing { internal_span } => Value::Nothing {
444 internal_span: *internal_span,
445 },
446 Value::Error {
447 error,
448 internal_span,
449 } => Value::Error {
450 error: error.clone(),
451 internal_span: *internal_span,
452 },
453 Value::Binary { val, internal_span } => Value::Binary {
454 val: val.clone(),
455 internal_span: *internal_span,
456 },
457 Value::CellPath { val, internal_span } => Value::CellPath {
458 val: val.clone(),
459 internal_span: *internal_span,
460 },
461 Value::Custom { val, internal_span } => val.clone_value(*internal_span),
462 }
463 }
464}
465
466enum CellPathMutation {
468 Upsert,
469 Update,
470 Insert { head_span: Span },
471 Remove { member: PathMember },
472}
473
474impl Value {
475 fn cant_convert_to<T>(&self, typ: &str) -> Result<T, ShellError> {
476 Err(ShellError::CantConvert {
477 to_type: typ.into(),
478 from_type: self.get_type().to_string(),
479 span: self.span(),
480 help: None,
481 })
482 }
483
484 pub fn as_bool(&self) -> Result<bool, ShellError> {
486 if let Value::Bool { val, .. } = self {
487 Ok(*val)
488 } else {
489 self.cant_convert_to("boolean")
490 }
491 }
492
493 pub fn as_int(&self) -> Result<i64, ShellError> {
495 if let Value::Int { val, .. } = self {
496 Ok(*val)
497 } else {
498 self.cant_convert_to("int")
499 }
500 }
501
502 pub fn as_float(&self) -> Result<f64, ShellError> {
504 if let Value::Float { val, .. } = self {
505 Ok(*val)
506 } else {
507 self.cant_convert_to("float")
508 }
509 }
510
511 pub fn coerce_float(&self) -> Result<f64, ShellError> {
527 match self {
528 Value::Float { val, .. } => Ok(*val),
529 Value::Int { val, .. } => Ok(*val as f64),
530 val => val.cant_convert_to("float"),
531 }
532 }
533
534 pub fn as_filesize(&self) -> Result<Filesize, ShellError> {
536 if let Value::Filesize { val, .. } = self {
537 Ok(*val)
538 } else {
539 self.cant_convert_to("filesize")
540 }
541 }
542
543 pub fn as_duration(&self) -> Result<i64, ShellError> {
545 if let Value::Duration { val, .. } = self {
546 Ok(*val)
547 } else {
548 self.cant_convert_to("duration")
549 }
550 }
551
552 pub fn as_date(&self) -> Result<DateTime<FixedOffset>, ShellError> {
554 if let Value::Date { val, .. } = self {
555 Ok(*val)
556 } else {
557 self.cant_convert_to("datetime")
558 }
559 }
560
561 pub fn as_range(&self) -> Result<Range, ShellError> {
563 if let Value::Range { val, .. } = self {
564 Ok(**val)
565 } else {
566 self.cant_convert_to("range")
567 }
568 }
569
570 pub fn into_range(self) -> Result<Range, ShellError> {
572 if let Value::Range { val, .. } = self {
573 Ok(*val)
574 } else {
575 self.cant_convert_to("range")
576 }
577 }
578
579 pub fn as_str(&self) -> Result<&str, ShellError> {
581 if let Value::String { val, .. } = self {
582 Ok(val)
583 } else {
584 self.cant_convert_to("string")
585 }
586 }
587
588 pub fn into_string(self) -> Result<String, ShellError> {
590 if let Value::String { val, .. } = self {
591 Ok(val)
592 } else {
593 self.cant_convert_to("string")
594 }
595 }
596
597 pub fn coerce_str(&self) -> Result<Cow<'_, str>, ShellError> {
627 match self {
628 Value::Bool { val, .. } => Ok(Cow::Owned(val.to_string())),
629 Value::Int { val, .. } => Ok(Cow::Owned(val.to_string())),
630 Value::Float { val, .. } => Ok(Cow::Owned(val.to_string())),
631 Value::String { val, .. } => Ok(Cow::Borrowed(val)),
632 Value::Glob { val, .. } => Ok(Cow::Borrowed(val)),
633 Value::Binary { val, .. } => match std::str::from_utf8(val) {
634 Ok(s) => Ok(Cow::Borrowed(s)),
635 Err(_) => self.cant_convert_to("string"),
636 },
637 Value::Date { val, .. } => Ok(Cow::Owned(
638 val.to_rfc3339_opts(chrono::SecondsFormat::Millis, true),
639 )),
640 val => val.cant_convert_to("string"),
641 }
642 }
643
644 pub fn coerce_string(&self) -> Result<String, ShellError> {
683 self.coerce_str().map(Cow::into_owned)
684 }
685
686 pub fn coerce_into_string(self) -> Result<String, ShellError> {
716 let span = self.span();
717 match self {
718 Value::Bool { val, .. } => Ok(val.to_string()),
719 Value::Int { val, .. } => Ok(val.to_string()),
720 Value::Float { val, .. } => Ok(val.to_string()),
721 Value::String { val, .. } => Ok(val),
722 Value::Glob { val, .. } => Ok(val),
723 Value::Binary { val, .. } => match String::from_utf8(val) {
724 Ok(s) => Ok(s),
725 Err(err) => Value::binary(err.into_bytes(), span).cant_convert_to("string"),
726 },
727 Value::Date { val, .. } => Ok(val.to_rfc3339_opts(chrono::SecondsFormat::Millis, true)),
728 val => val.cant_convert_to("string"),
729 }
730 }
731
732 pub fn as_char(&self) -> Result<char, ShellError> {
734 let span = self.span();
735 if let Value::String { val, .. } = self {
736 let mut chars = val.chars();
737 match (chars.next(), chars.next()) {
738 (Some(c), None) => Ok(c),
739 _ => Err(ShellError::MissingParameter {
740 param_name: "single character separator".into(),
741 span,
742 }),
743 }
744 } else {
745 self.cant_convert_to("char")
746 }
747 }
748
749 pub fn to_path(&self) -> Result<PathBuf, ShellError> {
751 if let Value::String { val, .. } = self {
752 Ok(PathBuf::from(val))
753 } else {
754 self.cant_convert_to("path")
755 }
756 }
757
758 pub fn as_record(&self) -> Result<&Record, ShellError> {
760 if let Value::Record { val, .. } = self {
761 Ok(val)
762 } else {
763 self.cant_convert_to("record")
764 }
765 }
766
767 pub fn into_record(self) -> Result<Record, ShellError> {
769 if let Value::Record { val, .. } = self {
770 Ok(val.into_owned())
771 } else {
772 self.cant_convert_to("record")
773 }
774 }
775
776 pub fn as_list(&self) -> Result<&[Value], ShellError> {
778 if let Value::List { vals, .. } = self {
779 Ok(vals)
780 } else {
781 self.cant_convert_to("list")
782 }
783 }
784
785 pub fn into_list(self) -> Result<Vec<Value>, ShellError> {
787 if let Value::List { vals, .. } = self {
788 Ok(vals)
789 } else {
790 self.cant_convert_to("list")
791 }
792 }
793
794 pub fn as_closure(&self) -> Result<&Closure, ShellError> {
796 if let Value::Closure { val, .. } = self {
797 Ok(val)
798 } else {
799 self.cant_convert_to("closure")
800 }
801 }
802
803 pub fn into_closure(self) -> Result<Closure, ShellError> {
805 if let Value::Closure { val, .. } = self {
806 Ok(*val)
807 } else {
808 self.cant_convert_to("closure")
809 }
810 }
811
812 pub fn as_binary(&self) -> Result<&[u8], ShellError> {
814 if let Value::Binary { val, .. } = self {
815 Ok(val)
816 } else {
817 self.cant_convert_to("binary")
818 }
819 }
820
821 pub fn into_binary(self) -> Result<Vec<u8>, ShellError> {
823 if let Value::Binary { val, .. } = self {
824 Ok(val)
825 } else {
826 self.cant_convert_to("binary")
827 }
828 }
829
830 pub fn coerce_binary(&self) -> Result<&[u8], ShellError> {
849 match self {
850 Value::Binary { val, .. } => Ok(val),
851 Value::String { val, .. } => Ok(val.as_bytes()),
852 val => val.cant_convert_to("binary"),
853 }
854 }
855
856 pub fn coerce_into_binary(self) -> Result<Vec<u8>, ShellError> {
872 match self {
873 Value::Binary { val, .. } => Ok(val),
874 Value::String { val, .. } => Ok(val.into_bytes()),
875 val => val.cant_convert_to("binary"),
876 }
877 }
878
879 pub fn as_cell_path(&self) -> Result<&CellPath, ShellError> {
881 if let Value::CellPath { val, .. } = self {
882 Ok(val)
883 } else {
884 self.cant_convert_to("cell path")
885 }
886 }
887
888 pub fn into_cell_path(self) -> Result<CellPath, ShellError> {
890 if let Value::CellPath { val, .. } = self {
891 Ok(val)
892 } else {
893 self.cant_convert_to("cell path")
894 }
895 }
896
897 pub fn coerce_bool(&self) -> Result<bool, ShellError> {
913 match self {
914 Value::Bool { val: false, .. } | Value::Int { val: 0, .. } | Value::Nothing { .. } => {
915 Ok(false)
916 }
917 Value::Float { val, .. } if val <= &f64::EPSILON => Ok(false),
918 Value::String { val, .. } => match val.trim().to_ascii_lowercase().as_str() {
919 "" | "0" | "false" => Ok(false),
920 _ => Ok(true),
921 },
922 Value::Bool { .. } | Value::Int { .. } | Value::Float { .. } => Ok(true),
923 _ => self.cant_convert_to("bool"),
924 }
925 }
926
927 pub fn as_custom_value(&self) -> Result<&dyn CustomValue, ShellError> {
929 if let Value::Custom { val, .. } = self {
930 Ok(val.as_ref())
931 } else {
932 self.cant_convert_to("custom value")
933 }
934 }
935
936 pub fn into_custom_value(self) -> Result<Box<dyn CustomValue>, ShellError> {
938 if let Value::Custom { val, .. } = self {
939 Ok(val)
940 } else {
941 self.cant_convert_to("custom value")
942 }
943 }
944
945 pub fn span(&self) -> Span {
947 match self {
948 Value::Bool { internal_span, .. }
949 | Value::Int { internal_span, .. }
950 | Value::Float { internal_span, .. }
951 | Value::Filesize { internal_span, .. }
952 | Value::Duration { internal_span, .. }
953 | Value::Date { internal_span, .. }
954 | Value::Range { internal_span, .. }
955 | Value::String { internal_span, .. }
956 | Value::Glob { internal_span, .. }
957 | Value::Record { internal_span, .. }
958 | Value::List { internal_span, .. }
959 | Value::Closure { internal_span, .. }
960 | Value::Nothing { internal_span, .. }
961 | Value::Binary { internal_span, .. }
962 | Value::CellPath { internal_span, .. }
963 | Value::Custom { internal_span, .. }
964 | Value::Error { internal_span, .. } => *internal_span,
965 }
966 }
967
968 pub fn set_span(&mut self, new_span: Span) {
970 match self {
971 Value::Bool { internal_span, .. }
972 | Value::Int { internal_span, .. }
973 | Value::Float { internal_span, .. }
974 | Value::Filesize { internal_span, .. }
975 | Value::Duration { internal_span, .. }
976 | Value::Date { internal_span, .. }
977 | Value::Range { internal_span, .. }
978 | Value::String { internal_span, .. }
979 | Value::Glob { internal_span, .. }
980 | Value::Record { internal_span, .. }
981 | Value::List { internal_span, .. }
982 | Value::Closure { internal_span, .. }
983 | Value::Nothing { internal_span, .. }
984 | Value::Binary { internal_span, .. }
985 | Value::CellPath { internal_span, .. }
986 | Value::Custom { internal_span, .. } => *internal_span = new_span,
987 Value::Error { .. } => (),
988 }
989 }
990
991 pub fn with_span(mut self, new_span: Span) -> Value {
993 self.set_span(new_span);
994 self
995 }
996
997 pub fn get_type(&self) -> Type {
999 match self {
1000 Value::Bool { .. } => Type::Bool,
1001 Value::Int { .. } => Type::Int,
1002 Value::Float { .. } => Type::Float,
1003 Value::Filesize { .. } => Type::Filesize,
1004 Value::Duration { .. } => Type::Duration,
1005 Value::Date { .. } => Type::Date,
1006 Value::Range { .. } => Type::Range,
1007 Value::String { .. } => Type::String,
1008 Value::Glob { .. } => Type::Glob,
1009 Value::Record { val, .. } => {
1010 Type::Record(val.iter().map(|(x, y)| (x.clone(), y.get_type())).collect())
1011 }
1012 Value::List { vals, .. } => {
1013 let ty = Type::supertype_of(vals.iter().map(Value::get_type)).unwrap_or(Type::Any);
1014
1015 match ty {
1016 Type::Record(columns) => Type::Table(columns),
1017 ty => Type::list(ty),
1018 }
1019 }
1020 Value::Nothing { .. } => Type::Nothing,
1021 Value::Closure { .. } => Type::Closure,
1022 Value::Error { .. } => Type::Error,
1023 Value::Binary { .. } => Type::Binary,
1024 Value::CellPath { .. } => Type::CellPath,
1025 Value::Custom { val, .. } => Type::Custom(val.type_name().into()),
1026 }
1027 }
1028
1029 pub fn get_type_shallow(&self) -> Type {
1032 match self {
1033 Value::Bool { .. } => Type::Bool,
1034 Value::Int { .. } => Type::Int,
1035 Value::Float { .. } => Type::Float,
1036 Value::Filesize { .. } => Type::Filesize,
1037 Value::Duration { .. } => Type::Duration,
1038 Value::Date { .. } => Type::Date,
1039 Value::Range { .. } => Type::Range,
1040 Value::String { .. } => Type::String,
1041 Value::Glob { .. } => Type::Glob,
1042 Value::Record { .. } => Type::record(),
1043 Value::List { .. } => Type::list(Type::Any),
1044 Value::Nothing { .. } => Type::Nothing,
1045 Value::Closure { .. } => Type::Closure,
1046 Value::Error { .. } => Type::Error,
1047 Value::Binary { .. } => Type::Binary,
1048 Value::CellPath { .. } => Type::CellPath,
1049 Value::Custom { val, .. } => Type::Custom(val.type_name().into()),
1050 }
1051 }
1052
1053 pub fn get_data_by_key(&self, name: &str) -> Option<Value> {
1054 let span = self.span();
1055 match self {
1056 Value::Record { val, .. } => val.get(name).cloned(),
1057 Value::List { vals, .. } => {
1058 let out = vals
1059 .iter()
1060 .map(|item| {
1061 item.as_record()
1062 .ok()
1063 .and_then(|val| val.get(name).cloned())
1064 .unwrap_or(Value::nothing(span))
1065 })
1066 .collect::<Vec<_>>();
1067
1068 if !out.is_empty() {
1069 Some(Value::list(out, span))
1070 } else {
1071 None
1072 }
1073 }
1074 _ => None,
1075 }
1076 }
1077
1078 fn format_datetime<Tz: TimeZone>(&self, date_time: &DateTime<Tz>, formatter: &str) -> String
1079 where
1080 Tz::Offset: Display,
1081 {
1082 let mut formatter_buf = String::new();
1083 let locale = get_locale_from_env_vars(Some("LC_TIME"), |name| std::env::var(name).ok())
1084 .and_then(|s| s.as_ref().try_into().ok())
1085 .unwrap_or(Locale::en_US);
1086 let format = date_time.format_localized(formatter, locale);
1087
1088 match formatter_buf.write_fmt(format_args!("{format}")) {
1089 Ok(_) => (),
1090 Err(_) => formatter_buf = format!("Invalid format string {formatter}"),
1091 }
1092 formatter_buf
1093 }
1094
1095 pub fn to_expanded_string(&self, separator: &str, config: &Config) -> String {
1100 let span = self.span();
1101 match self {
1102 Value::Bool { val, .. } => val.to_string(),
1103 Value::Int { val, .. } => val.to_string(),
1104 Value::Float { val, .. } => ObviousFloat(*val).to_string(),
1105 Value::Filesize { val, .. } => config.filesize.format(*val).to_string(),
1106 Value::Duration { val, .. } => format_duration(*val),
1107 Value::Date { val, .. } => match &config.datetime_format.normal {
1108 Some(format) => self.format_datetime(val, format),
1109 None => {
1110 format!(
1111 "{} ({})",
1112 if val.year() >= 0 && val.year() <= 9999 {
1113 val.to_rfc2822()
1114 } else {
1115 val.to_rfc3339()
1116 },
1117 human_time_from_now(val),
1118 )
1119 }
1120 },
1121 Value::Range { val, .. } => val.to_string(),
1122 Value::String { val, .. } => val.clone(),
1123 Value::Glob { val, .. } => val.clone(),
1124 Value::List { vals: val, .. } => format!(
1125 "[{}]",
1126 val.iter()
1127 .map(|x| x.to_expanded_string(", ", config))
1128 .collect::<Vec<_>>()
1129 .join(separator)
1130 ),
1131 Value::Record { val, .. } => format!(
1132 "{{{}}}",
1133 val.iter()
1134 .map(|(x, y)| format!("{}: {}", x, y.to_expanded_string(", ", config)))
1135 .collect::<Vec<_>>()
1136 .join(separator)
1137 ),
1138 Value::Closure { val, .. } => format!("closure_{}", val.block_id.get()),
1139 Value::Nothing { .. } => String::new(),
1140 Value::Error { error, .. } => format!("{error:?}"),
1141 Value::Binary { val, .. } => format!("{val:?}"),
1142 Value::CellPath { val, .. } => val.to_string(),
1143 Value::Custom { val, .. } => val
1146 .to_base_value(span)
1147 .map(|val| val.to_expanded_string(separator, config))
1148 .unwrap_or_else(|_| format!("<{}>", val.type_name())),
1149 }
1150 }
1151
1152 pub fn to_abbreviated_string(&self, config: &Config) -> String {
1160 match self {
1161 Value::Date { val, .. } => match &config.datetime_format.table {
1162 Some(format) => self.format_datetime(val, format),
1163 None => human_time_from_now(val).to_string(),
1164 },
1165 Value::List { vals, .. } => {
1166 if !vals.is_empty() && vals.iter().all(|x| matches!(x, Value::Record { .. })) {
1167 format!(
1168 "[table {} row{}]",
1169 vals.len(),
1170 if vals.len() == 1 { "" } else { "s" }
1171 )
1172 } else {
1173 format!(
1174 "[list {} item{}]",
1175 vals.len(),
1176 if vals.len() == 1 { "" } else { "s" }
1177 )
1178 }
1179 }
1180 Value::Record { val, .. } => format!(
1181 "{{record {} field{}}}",
1182 val.len(),
1183 if val.len() == 1 { "" } else { "s" }
1184 ),
1185 val => val.to_expanded_string(", ", config),
1186 }
1187 }
1188
1189 pub fn to_parsable_string(&self, separator: &str, config: &Config) -> String {
1199 match self {
1200 Value::String { val, .. } => format!("'{val}'"),
1202 Value::List { vals: val, .. } => format!(
1204 "[{}]",
1205 val.iter()
1206 .map(|x| x.to_parsable_string(", ", config))
1207 .collect::<Vec<_>>()
1208 .join(separator)
1209 ),
1210 Value::Record { val, .. } => format!(
1211 "{{{}}}",
1212 val.iter()
1213 .map(|(x, y)| format!("{}: {}", x, y.to_parsable_string(", ", config)))
1214 .collect::<Vec<_>>()
1215 .join(separator)
1216 ),
1217 _ => self.to_expanded_string(separator, config),
1219 }
1220 }
1221
1222 pub fn to_debug_string(&self) -> String {
1227 match self {
1228 Value::String { val, .. } => {
1229 if contains_emoji(val) {
1230 format!(
1232 "{:#?}",
1233 Value::string(val.escape_unicode().to_string(), self.span())
1234 )
1235 } else {
1236 format!("{self:-#?}")
1237 }
1238 }
1239 _ => format!("{self:-#?}"),
1240 }
1241 }
1242
1243 pub fn try_put_int_path_member_on_top(cell_paths: &[PathMember]) -> Option<Vec<PathMember>> {
1248 if !nu_experimental::REORDER_CELL_PATHS.get() {
1249 return None;
1250 }
1251 let idx = cell_paths
1252 .iter()
1253 .position(|pm| matches!(pm, PathMember::Int { .. }));
1254 idx.map(|idx| {
1255 let mut cell_paths = cell_paths.to_vec();
1256 cell_paths[0..idx + 1].rotate_right(1);
1257 cell_paths
1258 })
1259 }
1260
1261 pub fn follow_cell_path<'out>(
1263 &'out self,
1264 cell_path: &[PathMember],
1265 ) -> Result<Cow<'out, Value>, ShellError> {
1266 let mut store: Value = Value::test_nothing();
1269 let mut current: MultiLife<'out, '_, Value> = MultiLife::Out(self);
1270
1271 let reorder_cell_paths = nu_experimental::REORDER_CELL_PATHS.get();
1272
1273 let mut members: Vec<_> = if reorder_cell_paths {
1274 cell_path.iter().map(Some).collect()
1275 } else {
1276 Vec::new()
1277 };
1278 let mut members = members.as_mut_slice();
1279 let mut cell_path = cell_path;
1280
1281 loop {
1282 let member = if reorder_cell_paths {
1283 while let Some(None) = members.first() {
1285 members = &mut members[1..];
1286 }
1287
1288 if members.is_empty() {
1289 break;
1290 }
1291
1292 let member = if let Value::List { .. } = &*current {
1295 members
1297 .iter_mut()
1298 .find(|x| matches!(x, Some(PathMember::Int { .. })))
1299 .and_then(Option::take)
1301 } else {
1302 None
1303 };
1304
1305 let Some(member) = member.or_else(|| members.first_mut().and_then(Option::take))
1306 else {
1307 break;
1308 };
1309 member
1310 } else {
1311 match cell_path {
1312 [first, rest @ ..] => {
1313 cell_path = rest;
1314 first
1315 }
1316 _ => break,
1317 }
1318 };
1319
1320 current = match current {
1321 MultiLife::Out(current) => match get_value_member(current, member)? {
1322 ControlFlow::Break(span) => return Ok(Cow::Owned(Value::nothing(span))),
1323 ControlFlow::Continue(x) => match x {
1324 Cow::Borrowed(x) => MultiLife::Out(x),
1325 Cow::Owned(x) => {
1326 store = x;
1327 MultiLife::Local(&store)
1328 }
1329 },
1330 },
1331 MultiLife::Local(current) => match get_value_member(current, member)? {
1332 ControlFlow::Break(span) => return Ok(Cow::Owned(Value::nothing(span))),
1333 ControlFlow::Continue(x) => match x {
1334 Cow::Borrowed(x) => MultiLife::Local(x),
1335 Cow::Owned(x) => {
1336 store = x;
1337 MultiLife::Local(&store)
1338 }
1339 },
1340 },
1341 };
1342 }
1343
1344 if let Value::Error { error, .. } = &*current {
1347 Err(error.as_ref().clone())
1348 } else {
1349 Ok(match current {
1350 MultiLife::Out(x) => Cow::Borrowed(x),
1351 MultiLife::Local(x) => {
1352 let x = if std::ptr::eq(x, &store) {
1353 store
1354 } else {
1355 x.clone()
1356 };
1357 Cow::Owned(x)
1358 }
1359 })
1360 }
1361 }
1362
1363 pub fn upsert_cell_path(
1365 &mut self,
1366 cell_path: &[PathMember],
1367 callback: Box<dyn FnOnce(&Value) -> Value>,
1368 ) -> Result<(), ShellError> {
1369 let new_val = callback(self.follow_cell_path(cell_path)?.as_ref());
1370
1371 match new_val {
1372 Value::Error { error, .. } => Err(*error),
1373 new_val => self.upsert_data_at_cell_path(cell_path, new_val),
1374 }
1375 }
1376
1377 pub fn upsert_data_at_cell_path(
1378 &mut self,
1379 cell_path: &[PathMember],
1380 new_val: Value,
1381 ) -> Result<(), ShellError> {
1382 self.mutate_data_at_cell_path(cell_path, new_val, &CellPathMutation::Upsert)
1383 }
1384
1385 pub fn update_cell_path<'a>(
1387 &mut self,
1388 cell_path: &[PathMember],
1389 callback: Box<dyn FnOnce(&Value) -> Value + 'a>,
1390 ) -> Result<(), ShellError> {
1391 let new_val = callback(self.follow_cell_path(cell_path)?.as_ref());
1392
1393 match new_val {
1394 Value::Error { error, .. } => Err(*error),
1395 new_val => self.update_data_at_cell_path(cell_path, new_val),
1396 }
1397 }
1398
1399 pub fn update_data_at_cell_path(
1400 &mut self,
1401 cell_path: &[PathMember],
1402 new_val: Value,
1403 ) -> Result<(), ShellError> {
1404 self.mutate_data_at_cell_path(cell_path, new_val, &CellPathMutation::Update)
1405 }
1406
1407 pub fn remove_data_at_cell_path(&mut self, cell_path: &[PathMember]) -> Result<(), ShellError> {
1408 let Some((member, path)) = cell_path.split_last() else {
1409 return Ok(());
1410 };
1411 self.mutate_data_at_cell_path(
1412 path,
1413 Value::nothing(Span::unknown()),
1414 &CellPathMutation::Remove {
1415 member: member.clone(),
1416 },
1417 )
1418 }
1419
1420 pub fn insert_data_at_cell_path(
1421 &mut self,
1422 cell_path: &[PathMember],
1423 new_val: Value,
1424 head_span: Span,
1425 ) -> Result<(), ShellError> {
1426 self.mutate_data_at_cell_path(cell_path, new_val, &CellPathMutation::Insert { head_span })
1427 }
1428
1429 fn remove_member(&mut self, member: &PathMember) -> Result<(), ShellError> {
1431 let v_span = self.span();
1432 match member {
1433 PathMember::String {
1434 val: col_name,
1435 span,
1436 optional,
1437 casing,
1438 } => match self {
1439 Value::List { vals, .. } => {
1440 for val in vals.iter_mut() {
1441 let v_span = val.span();
1442 match val {
1443 Value::Record { val: record, .. } => {
1444 let value = record.to_mut().cased_mut(*casing).remove(col_name);
1445 if value.is_none() && !optional {
1446 return Err(ShellError::CantFindColumn {
1447 col_name: col_name.clone(),
1448 span: Some(*span),
1449 src_span: v_span,
1450 });
1451 }
1452 }
1453 v => {
1454 return Err(ShellError::CantFindColumn {
1455 col_name: col_name.clone(),
1456 span: Some(*span),
1457 src_span: v.span(),
1458 });
1459 }
1460 }
1461 }
1462 Ok(())
1463 }
1464 Value::Record { val: record, .. } => {
1465 if record
1466 .to_mut()
1467 .cased_mut(*casing)
1468 .remove(col_name)
1469 .is_none()
1470 && !optional
1471 {
1472 return Err(ShellError::CantFindColumn {
1473 col_name: col_name.clone(),
1474 span: Some(*span),
1475 src_span: v_span,
1476 });
1477 }
1478 Ok(())
1479 }
1480 v => Err(ShellError::CantFindColumn {
1481 col_name: col_name.clone(),
1482 span: Some(*span),
1483 src_span: v.span(),
1484 }),
1485 },
1486 PathMember::Int {
1487 val: row_num,
1488 span,
1489 optional,
1490 } => match self {
1491 Value::List { vals, .. } => {
1492 if vals.get_mut(*row_num).is_some() {
1493 vals.remove(*row_num);
1494 Ok(())
1495 } else if *optional {
1496 Ok(())
1497 } else if vals.is_empty() {
1498 Err(ShellError::AccessEmptyContent { span: *span })
1499 } else {
1500 Err(ShellError::AccessBeyondEnd {
1501 max_idx: vals.len() - 1,
1502 span: *span,
1503 })
1504 }
1505 }
1506 v => Err(ShellError::NotAList {
1507 dst_span: *span,
1508 src_span: v.span(),
1509 }),
1510 },
1511 }
1512 }
1513
1514 fn mutate_record_at_string_member(
1515 record: &mut Record,
1516 member: &PathMember,
1517 src_span: Span,
1518 path: &[PathMember],
1519 new_val: Value,
1520 action: &CellPathMutation,
1521 ) -> Result<(), ShellError> {
1522 let PathMember::String {
1523 val: col_name,
1524 span,
1525 casing,
1526 optional,
1527 } = member
1528 else {
1529 return Err(ShellError::NushellFailed {
1530 msg: "mutate_record_at_string_member called with non-String PathMember".into(),
1531 });
1532 };
1533 if let Some(val) = record.cased_mut(*casing).get_mut(col_name) {
1534 if path.is_empty() && matches!(action, CellPathMutation::Insert { .. }) {
1535 return Err(ShellError::ColumnAlreadyExists {
1536 col_name: col_name.to_owned(),
1537 span: *span,
1538 src_span,
1539 });
1540 }
1541 val.mutate_data_at_cell_path(path, new_val, action)
1542 } else {
1543 match action {
1544 CellPathMutation::Update | CellPathMutation::Remove { .. } => {
1545 if !optional {
1546 return Err(ShellError::CantFindColumn {
1547 col_name: col_name.to_owned(),
1548 span: Some(*span),
1549 src_span,
1550 });
1551 }
1552 Ok(())
1553 }
1554 _ => {
1555 let new_col = Value::with_data_at_cell_path(path, new_val)?;
1556 record.push(col_name, new_col);
1557 Ok(())
1558 }
1559 }
1560 }
1561 }
1562
1563 fn mutate_data_at_cell_path(
1564 &mut self,
1565 cell_path: &[PathMember],
1566 new_val: Value,
1567 action: &CellPathMutation,
1568 ) -> Result<(), ShellError> {
1569 let v_span = self.span();
1570 let Some((member, path)) = cell_path.split_first() else {
1571 match action {
1572 CellPathMutation::Remove { member } => return self.remove_member(member),
1573 _ => {
1574 *self = new_val;
1575 return Ok(());
1576 }
1577 }
1578 };
1579
1580 match member {
1581 PathMember::String {
1582 val: col_name,
1583 span,
1584 optional,
1585 ..
1586 } => match self {
1587 Value::List { vals, .. } => {
1588 if !matches!(action, CellPathMutation::Remove { .. })
1589 && let Some(new_cell_path) = Self::try_put_int_path_member_on_top(cell_path)
1590 {
1591 self.mutate_data_at_cell_path(&new_cell_path, new_val.clone(), action)?;
1592 } else {
1593 for val in vals.iter_mut() {
1594 let v_span = val.span();
1595 match val {
1596 Value::Record { val: record, .. } => {
1597 Self::mutate_record_at_string_member(
1598 record.to_mut(),
1599 member,
1600 v_span,
1601 path,
1602 new_val.clone(),
1603 action,
1604 )?;
1605 }
1606 Value::Error { error, .. } => return Err(*error.clone()),
1607 v => match action {
1608 CellPathMutation::Insert { head_span } => {
1609 return Err(ShellError::UnsupportedInput {
1610 msg: "expected table or record".into(),
1611 input: format!("input type: {:?}", v.get_type()),
1612 msg_span: *head_span,
1613 input_span: *span,
1614 });
1615 }
1616 CellPathMutation::Update | CellPathMutation::Remove { .. } => {
1617 if !*optional {
1618 return Err(ShellError::CantFindColumn {
1619 col_name: col_name.clone(),
1620 span: Some(*span),
1621 src_span: v.span(),
1622 });
1623 }
1624 }
1625 CellPathMutation::Upsert => {
1626 return Err(ShellError::CantFindColumn {
1627 col_name: col_name.clone(),
1628 span: Some(*span),
1629 src_span: v.span(),
1630 });
1631 }
1632 },
1633 }
1634 }
1635 }
1636 }
1637 Value::Record { val: record, .. } => {
1638 Self::mutate_record_at_string_member(
1639 record.to_mut(),
1640 member,
1641 v_span,
1642 path,
1643 new_val,
1644 action,
1645 )?;
1646 }
1647 Value::Error { error, .. } => return Err(*error.clone()),
1648 v => match action {
1649 CellPathMutation::Insert { head_span } => {
1650 return Err(ShellError::UnsupportedInput {
1651 msg: "table or record".into(),
1652 input: format!("input type: {:?}", v.get_type()),
1653 msg_span: *head_span,
1654 input_span: *span,
1655 });
1656 }
1657 CellPathMutation::Update | CellPathMutation::Remove { .. } => {
1658 if !*optional {
1659 return Err(ShellError::CantFindColumn {
1660 col_name: col_name.clone(),
1661 span: Some(*span),
1662 src_span: v.span(),
1663 });
1664 }
1665 }
1666 CellPathMutation::Upsert => {
1667 return Err(ShellError::CantFindColumn {
1668 col_name: col_name.clone(),
1669 span: Some(*span),
1670 src_span: v.span(),
1671 });
1672 }
1673 },
1674 },
1675 PathMember::Int {
1676 val: row_num,
1677 span,
1678 optional,
1679 } => match self {
1680 Value::List { vals, .. } => {
1681 if let Some(v) = vals.get_mut(*row_num) {
1682 if path.is_empty() && matches!(action, CellPathMutation::Insert { .. }) {
1683 vals.insert(*row_num, new_val);
1684 } else {
1685 v.mutate_data_at_cell_path(path, new_val, action)?;
1686 }
1687 } else {
1688 match action {
1689 CellPathMutation::Upsert | CellPathMutation::Insert { .. } => {
1690 if vals.len() != *row_num {
1691 return Err(ShellError::InsertAfterNextFreeIndex {
1692 available_idx: vals.len(),
1693 span: *span,
1694 });
1695 }
1696 vals.push(Value::with_data_at_cell_path(path, new_val)?);
1697 }
1698 CellPathMutation::Update | CellPathMutation::Remove { .. } => {
1699 if !*optional {
1700 if vals.is_empty() {
1701 return Err(ShellError::AccessEmptyContent { span: *span });
1702 } else {
1703 return Err(ShellError::AccessBeyondEnd {
1704 max_idx: vals.len() - 1,
1705 span: *span,
1706 });
1707 }
1708 }
1709 }
1710 }
1711 }
1712 }
1713 Value::Error { error, .. } => return Err(*error.clone()),
1714 _ => {
1715 return Err(ShellError::NotAList {
1716 dst_span: *span,
1717 src_span: v_span,
1718 });
1719 }
1720 },
1721 }
1722 Ok(())
1723 }
1724
1725 fn with_data_at_cell_path(cell_path: &[PathMember], value: Value) -> Result<Value, ShellError> {
1728 if let Some((member, path)) = cell_path.split_first() {
1729 let span = value.span();
1730 match member {
1731 PathMember::String { val, .. } => Ok(Value::record(
1732 std::iter::once((val.clone(), Value::with_data_at_cell_path(path, value)?))
1733 .collect(),
1734 span,
1735 )),
1736 PathMember::Int { val, .. } => {
1737 if *val == 0usize {
1738 Ok(Value::list(
1739 vec![Value::with_data_at_cell_path(path, value)?],
1740 span,
1741 ))
1742 } else {
1743 Err(ShellError::InsertAfterNextFreeIndex {
1744 available_idx: 0,
1745 span,
1746 })
1747 }
1748 }
1749 }
1750 } else {
1751 Ok(value)
1752 }
1753 }
1754
1755 pub fn recurse_mut<E>(
1762 &mut self,
1763 f: &mut impl FnMut(&mut Value) -> Result<(), E>,
1764 ) -> Result<(), E> {
1765 f(self)?;
1767 match self {
1769 Value::Record { val, .. } => val
1770 .to_mut()
1771 .iter_mut()
1772 .try_for_each(|(_, rec_value)| rec_value.recurse_mut(f)),
1773 Value::List { vals, .. } => vals
1774 .iter_mut()
1775 .try_for_each(|list_value| list_value.recurse_mut(f)),
1776 Value::Closure { val, .. } => val
1779 .captures
1780 .iter_mut()
1781 .map(|(_, captured_value)| captured_value)
1782 .try_for_each(|captured_value| captured_value.recurse_mut(f)),
1783 Value::Bool { .. }
1785 | Value::Int { .. }
1786 | Value::Float { .. }
1787 | Value::Filesize { .. }
1788 | Value::Duration { .. }
1789 | Value::Date { .. }
1790 | Value::Range { .. }
1791 | Value::String { .. }
1792 | Value::Glob { .. }
1793 | Value::Nothing { .. }
1794 | Value::Error { .. }
1795 | Value::Binary { .. }
1796 | Value::CellPath { .. } => Ok(()),
1797 Value::Custom { .. } => Ok(()),
1799 }
1800 }
1801
1802 pub fn is_empty(&self) -> bool {
1804 match self {
1805 Value::String { val, .. } => val.is_empty(),
1806 Value::List { vals, .. } => vals.is_empty(),
1807 Value::Record { val, .. } => val.is_empty(),
1808 Value::Binary { val, .. } => val.is_empty(),
1809 Value::Nothing { .. } => true,
1810 _ => false,
1811 }
1812 }
1813
1814 pub fn is_nothing(&self) -> bool {
1815 matches!(self, Value::Nothing { .. })
1816 }
1817
1818 pub fn is_error(&self) -> bool {
1819 matches!(self, Value::Error { .. })
1820 }
1821
1822 pub fn unwrap_error(self) -> Result<Self, ShellError> {
1824 match self {
1825 Self::Error { error, .. } => Err(*error),
1826 val => Ok(val),
1827 }
1828 }
1829
1830 pub fn is_true(&self) -> bool {
1831 matches!(self, Value::Bool { val: true, .. })
1832 }
1833
1834 pub fn is_false(&self) -> bool {
1835 matches!(self, Value::Bool { val: false, .. })
1836 }
1837
1838 pub fn columns(&self) -> impl Iterator<Item = &String> {
1839 let opt = match self {
1840 Value::Record { val, .. } => Some(val.columns()),
1841 _ => None,
1842 };
1843
1844 opt.into_iter().flatten()
1845 }
1846
1847 pub fn memory_size(&self) -> usize {
1849 match self {
1850 Value::Bool { .. } => std::mem::size_of::<Self>(),
1851 Value::Int { .. } => std::mem::size_of::<Self>(),
1852 Value::Float { .. } => std::mem::size_of::<Self>(),
1853 Value::Filesize { .. } => std::mem::size_of::<Self>(),
1854 Value::Duration { .. } => std::mem::size_of::<Self>(),
1855 Value::Date { .. } => std::mem::size_of::<Self>(),
1856 Value::Range { val, .. } => std::mem::size_of::<Self>() + val.memory_size(),
1857 Value::String { val, .. } => std::mem::size_of::<Self>() + val.capacity(),
1858 Value::Glob { val, .. } => std::mem::size_of::<Self>() + val.capacity(),
1859 Value::Record { val, .. } => std::mem::size_of::<Self>() + val.memory_size(),
1860 Value::List { vals, .. } => {
1861 std::mem::size_of::<Self>() + vals.iter().map(|v| v.memory_size()).sum::<usize>()
1862 }
1863 Value::Closure { val, .. } => std::mem::size_of::<Self>() + val.memory_size(),
1864 Value::Nothing { .. } => std::mem::size_of::<Self>(),
1865 Value::Error { error, .. } => {
1866 std::mem::size_of::<Self>() + std::mem::size_of_val(error)
1867 }
1868 Value::Binary { val, .. } => std::mem::size_of::<Self>() + val.capacity(),
1869 Value::CellPath { val, .. } => std::mem::size_of::<Self>() + val.memory_size(),
1870 Value::Custom { val, .. } => std::mem::size_of::<Self>() + val.memory_size(),
1871 }
1872 }
1873
1874 pub fn bool(val: bool, span: Span) -> Value {
1875 Value::Bool {
1876 val,
1877 internal_span: span,
1878 }
1879 }
1880
1881 pub fn int(val: i64, span: Span) -> Value {
1882 Value::Int {
1883 val,
1884 internal_span: span,
1885 }
1886 }
1887
1888 pub fn float(val: f64, span: Span) -> Value {
1889 Value::Float {
1890 val,
1891 internal_span: span,
1892 }
1893 }
1894
1895 pub fn filesize(val: impl Into<Filesize>, span: Span) -> Value {
1896 Value::Filesize {
1897 val: val.into(),
1898 internal_span: span,
1899 }
1900 }
1901
1902 pub fn duration(val: i64, span: Span) -> Value {
1903 Value::Duration {
1904 val,
1905 internal_span: span,
1906 }
1907 }
1908
1909 pub fn date(val: DateTime<FixedOffset>, span: Span) -> Value {
1910 Value::Date {
1911 val,
1912 internal_span: span,
1913 }
1914 }
1915
1916 pub fn range(val: Range, span: Span) -> Value {
1917 Value::Range {
1918 val: val.into(),
1919 signals: None,
1920 internal_span: span,
1921 }
1922 }
1923
1924 pub fn string(val: impl Into<String>, span: Span) -> Value {
1925 Value::String {
1926 val: val.into(),
1927 internal_span: span,
1928 }
1929 }
1930
1931 pub fn glob(val: impl Into<String>, no_expand: bool, span: Span) -> Value {
1932 Value::Glob {
1933 val: val.into(),
1934 no_expand,
1935 internal_span: span,
1936 }
1937 }
1938
1939 pub fn record(val: Record, span: Span) -> Value {
1940 Value::Record {
1941 val: SharedCow::new(val),
1942 internal_span: span,
1943 }
1944 }
1945
1946 pub fn list(vals: Vec<Value>, span: Span) -> Value {
1947 Value::List {
1948 vals,
1949 signals: None,
1950 internal_span: span,
1951 }
1952 }
1953
1954 pub fn closure(val: Closure, span: Span) -> Value {
1955 Value::Closure {
1956 val: val.into(),
1957 internal_span: span,
1958 }
1959 }
1960
1961 pub fn nothing(span: Span) -> Value {
1963 Value::Nothing {
1964 internal_span: span,
1965 }
1966 }
1967
1968 pub fn error(error: ShellError, span: Span) -> Value {
1969 Value::Error {
1970 error: Box::new(error),
1971 internal_span: span,
1972 }
1973 }
1974
1975 pub fn binary(val: impl Into<Vec<u8>>, span: Span) -> Value {
1976 Value::Binary {
1977 val: val.into(),
1978 internal_span: span,
1979 }
1980 }
1981
1982 pub fn cell_path(val: CellPath, span: Span) -> Value {
1983 Value::CellPath {
1984 val,
1985 internal_span: span,
1986 }
1987 }
1988
1989 pub fn custom(val: Box<dyn CustomValue>, span: Span) -> Value {
1990 Value::Custom {
1991 val,
1992 internal_span: span,
1993 }
1994 }
1995
1996 pub fn test_bool(val: bool) -> Value {
1999 Value::bool(val, Span::test_data())
2000 }
2001
2002 pub fn test_int(val: i64) -> Value {
2005 Value::int(val, Span::test_data())
2006 }
2007
2008 pub fn test_float(val: f64) -> Value {
2011 Value::float(val, Span::test_data())
2012 }
2013
2014 pub fn test_filesize(val: impl Into<Filesize>) -> Value {
2017 Value::filesize(val, Span::test_data())
2018 }
2019
2020 pub fn test_duration(val: i64) -> Value {
2023 Value::duration(val, Span::test_data())
2024 }
2025
2026 pub fn test_date(val: DateTime<FixedOffset>) -> Value {
2029 Value::date(val, Span::test_data())
2030 }
2031
2032 pub fn test_range(val: Range) -> Value {
2035 Value::range(val, Span::test_data())
2036 }
2037
2038 pub fn test_string(val: impl Into<String>) -> Value {
2041 Value::string(val, Span::test_data())
2042 }
2043
2044 pub fn test_glob(val: impl Into<String>) -> Value {
2047 Value::glob(val, false, Span::test_data())
2048 }
2049
2050 pub fn test_record(val: Record) -> Value {
2053 Value::record(val, Span::test_data())
2054 }
2055
2056 pub fn test_list(vals: Vec<Value>) -> Value {
2059 Value::list(vals, Span::test_data())
2060 }
2061
2062 pub fn test_closure(val: Closure) -> Value {
2065 Value::closure(val, Span::test_data())
2066 }
2067
2068 pub fn test_nothing() -> Value {
2071 Value::nothing(Span::test_data())
2072 }
2073
2074 pub fn test_binary(val: impl Into<Vec<u8>>) -> Value {
2077 Value::binary(val, Span::test_data())
2078 }
2079
2080 pub fn test_cell_path(val: CellPath) -> Value {
2083 Value::cell_path(val, Span::test_data())
2084 }
2085
2086 pub fn test_custom_value(val: Box<dyn CustomValue>) -> Value {
2089 Value::custom(val, Span::test_data())
2090 }
2091
2092 pub fn test_values() -> Vec<Value> {
2098 vec![
2099 Value::test_bool(false),
2100 Value::test_int(0),
2101 Value::test_filesize(0),
2102 Value::test_duration(0),
2103 Value::test_date(DateTime::UNIX_EPOCH.into()),
2104 Value::test_range(Range::IntRange(IntRange {
2105 start: 0,
2106 step: 1,
2107 end: Bound::Excluded(0),
2108 })),
2109 Value::test_float(0.0),
2110 Value::test_string(String::new()),
2111 Value::test_record(Record::new()),
2112 Value::test_list(Vec::new()),
2113 Value::test_closure(Closure {
2114 block_id: BlockId::new(0),
2115 captures: Vec::new(),
2116 }),
2117 Value::test_nothing(),
2118 Value::error(
2119 ShellError::NushellFailed { msg: String::new() },
2120 Span::test_data(),
2121 ),
2122 Value::test_binary(Vec::new()),
2123 Value::test_cell_path(CellPath {
2124 members: Vec::new(),
2125 }),
2126 ]
2128 }
2129
2130 #[track_caller]
2136 pub fn assert_eq(&self, other: impl IntoValue) {
2137 let other = other.into_value(Span::test_data());
2138 assert_eq!(self, &other)
2139 }
2140
2141 pub fn inject_signals(&mut self, engine_state: &EngineState) {
2144 match self {
2145 Value::List { signals: s, .. } | Value::Range { signals: s, .. } => {
2146 *s = Some(engine_state.signals().clone());
2147 }
2148 _ => (),
2149 }
2150 }
2151}
2152
2153impl CompareTypes<Type> for Value {
2154 fn compare_types(&self, other: &Type) -> Option<TypeRelation> {
2155 match other {
2156 Type::Any => return Some(TypeRelation::Subtype),
2157 Type::OneOf(oneof) => {
2158 return oneof
2159 .iter()
2160 .any(|ty| self.is_subtype_of(ty))
2161 .then_some(TypeRelation::Subtype);
2162 }
2163 _ => (),
2164 }
2165
2166 match self {
2167 Value::List { vals, .. } => match other {
2168 Type::List(ty) if let Type::Any = ty.as_ref() => Some(TypeRelation::Subtype),
2169 Type::List(ty) => {
2170 let ty = ty.as_ref();
2171 vals.iter()
2172 .map(|val| val.compare_types(ty))
2173 .try_fold(TypeRelation::Equal, |acc, e| acc.combine(e?))
2174 }
2175 Type::Table(cols) => vals
2176 .iter()
2177 .map(|val| val.as_record().ok().and_then(|rec| rec.compare_types(cols)))
2178 .try_fold(TypeRelation::Equal, |acc, e| acc.combine(e?)),
2179 _ => None,
2180 },
2181 Value::Record { val, .. } => match other {
2182 Type::Record(cols) => val.compare_types(cols),
2183 _ => None,
2184 },
2185 val => val.get_type().compare_types(other),
2186 }
2187 }
2188
2189 fn is_subtype_of(&self, other: &Type) -> bool {
2204 matches!(
2205 self.compare_types(other),
2206 Some(TypeRelation::Subtype | TypeRelation::Equal)
2207 )
2208 }
2209}
2210
2211fn get_value_member<'a>(
2212 current: &'a Value,
2213 member: &PathMember,
2214) -> Result<ControlFlow<Span, Cow<'a, Value>>, ShellError> {
2215 match member {
2216 PathMember::Int {
2217 val: count,
2218 span: origin_span,
2219 optional,
2220 } => {
2221 match current {
2223 Value::List { vals, .. } => {
2224 if *count < vals.len() {
2225 Ok(ControlFlow::Continue(Cow::Borrowed(&vals[*count])))
2226 } else if *optional {
2227 Ok(ControlFlow::Break(*origin_span))
2228 } else if vals.is_empty() {
2230 Err(ShellError::AccessEmptyContent { span: *origin_span })
2231 } else {
2232 Err(ShellError::AccessBeyondEnd {
2233 max_idx: vals.len() - 1,
2234 span: *origin_span,
2235 })
2236 }
2237 }
2238 Value::Binary { val, .. } => {
2239 if let Some(item) = val.get(*count) {
2240 Ok(ControlFlow::Continue(Cow::Owned(Value::int(
2241 *item as i64,
2242 *origin_span,
2243 ))))
2244 } else if *optional {
2245 Ok(ControlFlow::Break(*origin_span))
2246 } else if val.is_empty() {
2248 Err(ShellError::AccessEmptyContent { span: *origin_span })
2249 } else {
2250 Err(ShellError::AccessBeyondEnd {
2251 max_idx: val.len() - 1,
2252 span: *origin_span,
2253 })
2254 }
2255 }
2256 Value::Range { val, .. } => {
2257 if let Some(item) = val
2258 .into_range_iter(current.span(), Signals::empty())
2259 .nth(*count)
2260 {
2261 Ok(ControlFlow::Continue(Cow::Owned(item)))
2262 } else if *optional {
2263 Ok(ControlFlow::Break(*origin_span))
2264 } else {
2266 Err(ShellError::AccessBeyondEndOfStream {
2267 span: *origin_span,
2268 })
2269 }
2270 }
2271 Value::Custom { val, .. } => {
2272 match val.follow_path_int(current.span(), *count, *origin_span, *optional)
2273 {
2274 Ok(val) => Ok(ControlFlow::Continue(Cow::Owned(val))),
2275 Err(err) => {
2276 if *optional {
2277 Ok(ControlFlow::Break(*origin_span))
2278 } else {
2280 Err(err)
2281 }
2282 }
2283 }
2284 }
2285 Value::Nothing { .. } if *optional => Ok(ControlFlow::Break(*origin_span)),
2286 Value::Record { .. } => Err(ShellError::TypeMismatch {
2289 err_message:"Can't access record values with a row index. Try specifying a column name instead".into(),
2290 span: *origin_span,
2291 }),
2292 Value::Error { error, .. } => Err(*error.clone()),
2293 x => Err(ShellError::IncompatiblePathAccess { type_name: format!("{}", x.get_type()), span: *origin_span }),
2294 }
2295 }
2296 PathMember::String {
2297 val: column_name,
2298 span: origin_span,
2299 optional,
2300 casing,
2301 } => {
2302 let span = current.span();
2303 match current {
2304 Value::Record { val, .. } => {
2305 let found = val.cased(*casing).get(column_name);
2306 if let Some(found) = found {
2307 Ok(ControlFlow::Continue(Cow::Borrowed(found)))
2308 } else if *optional {
2309 Ok(ControlFlow::Break(*origin_span))
2310 } else if let Some(suggestion) = did_you_mean(val.columns(), column_name) {
2312 Err(ShellError::DidYouMean {
2313 suggestion,
2314 span: *origin_span,
2315 })
2316 } else {
2317 Err(ShellError::CantFindColumn {
2318 col_name: column_name.clone(),
2319 span: Some(*origin_span),
2320 src_span: span,
2321 })
2322 }
2323 }
2324 Value::List { vals, .. } => {
2328 let list = vals
2329 .iter()
2330 .map(|val| {
2331 let val_span = val.span();
2332 match val {
2333 Value::Record { val, .. } => {
2334 let found = val.cased(*casing).get(column_name);
2335 if let Some(found) = found {
2336 Ok(found.clone())
2337 } else if *optional {
2338 Ok(Value::nothing(*origin_span))
2339 } else if let Some(suggestion) =
2340 did_you_mean(val.columns(), column_name)
2341 {
2342 Err(ShellError::DidYouMean {
2343 suggestion,
2344 span: *origin_span,
2345 })
2346 } else {
2347 Err(ShellError::CantFindColumn {
2348 col_name: column_name.clone(),
2349 span: Some(*origin_span),
2350 src_span: val_span,
2351 })
2352 }
2353 }
2354 Value::Nothing { .. } if *optional => {
2355 Ok(Value::nothing(*origin_span))
2356 }
2357 _ => Err(ShellError::CantFindColumn {
2358 col_name: column_name.clone(),
2359 span: Some(*origin_span),
2360 src_span: val_span,
2361 }),
2362 }
2363 })
2364 .collect::<Result<_, _>>()?;
2365
2366 Ok(ControlFlow::Continue(Cow::Owned(Value::list(list, span))))
2367 }
2368 Value::Custom { val, .. } => {
2369 match val.follow_path_string(
2370 current.span(),
2371 column_name.clone(),
2372 *origin_span,
2373 *optional,
2374 *casing,
2375 ) {
2376 Ok(val) => Ok(ControlFlow::Continue(Cow::Owned(val))),
2377 Err(err) => {
2378 if *optional {
2379 Ok(ControlFlow::Break(*origin_span))
2380 } else {
2382 Err(err)
2383 }
2384 }
2385 }
2386 }
2387 Value::Nothing { .. } if *optional => Ok(ControlFlow::Break(*origin_span)),
2388 Value::Error { error, .. } => Err(error.as_ref().clone()),
2389 x => Err(ShellError::IncompatiblePathAccess {
2390 type_name: format!("{}", x.get_type()),
2391 span: *origin_span,
2392 }),
2393 }
2394 }
2395 }
2396}
2397
2398impl Default for Value {
2399 fn default() -> Self {
2400 Value::Nothing {
2401 internal_span: Span::unknown(),
2402 }
2403 }
2404}
2405
2406impl PartialOrd for Value {
2407 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
2408 fn compare_floats(val: f64, other: f64) -> Option<Ordering> {
2414 let prec = f64::EPSILON.max(val.abs().max(other.abs()) * f64::EPSILON);
2415
2416 if (other - val).abs() <= prec {
2417 return Some(Ordering::Equal);
2418 }
2419
2420 val.partial_cmp(&other)
2421 }
2422
2423 match (self, other) {
2424 (Value::Bool { val: lhs, .. }, rhs) => match rhs {
2425 Value::Bool { val: rhs, .. } => lhs.partial_cmp(rhs),
2426 Value::Int { .. } => Some(Ordering::Less),
2427 Value::Float { .. } => Some(Ordering::Less),
2428 Value::String { .. } => Some(Ordering::Less),
2429 Value::Glob { .. } => Some(Ordering::Less),
2430 Value::Filesize { .. } => Some(Ordering::Less),
2431 Value::Duration { .. } => Some(Ordering::Less),
2432 Value::Date { .. } => Some(Ordering::Less),
2433 Value::Range { .. } => Some(Ordering::Less),
2434 Value::Record { .. } => Some(Ordering::Less),
2435 Value::List { .. } => Some(Ordering::Less),
2436 Value::Closure { .. } => Some(Ordering::Less),
2437 Value::Error { .. } => Some(Ordering::Less),
2438 Value::Binary { .. } => Some(Ordering::Less),
2439 Value::CellPath { .. } => Some(Ordering::Less),
2440 Value::Custom { .. } => Some(Ordering::Less),
2441 Value::Nothing { .. } => Some(Ordering::Less),
2442 },
2443 (Value::Int { val: lhs, .. }, rhs) => match rhs {
2444 Value::Bool { .. } => Some(Ordering::Greater),
2445 Value::Int { val: rhs, .. } => lhs.partial_cmp(rhs),
2446 Value::Float { val: rhs, .. } => compare_floats(*lhs as f64, *rhs),
2447 Value::String { .. } => Some(Ordering::Less),
2448 Value::Glob { .. } => Some(Ordering::Less),
2449 Value::Filesize { .. } => Some(Ordering::Less),
2450 Value::Duration { .. } => Some(Ordering::Less),
2451 Value::Date { .. } => Some(Ordering::Less),
2452 Value::Range { .. } => Some(Ordering::Less),
2453 Value::Record { .. } => Some(Ordering::Less),
2454 Value::List { .. } => Some(Ordering::Less),
2455 Value::Closure { .. } => Some(Ordering::Less),
2456 Value::Error { .. } => Some(Ordering::Less),
2457 Value::Binary { .. } => Some(Ordering::Less),
2458 Value::CellPath { .. } => Some(Ordering::Less),
2459 Value::Custom { .. } => Some(Ordering::Less),
2460 Value::Nothing { .. } => Some(Ordering::Less),
2461 },
2462 (Value::Float { val: lhs, .. }, rhs) => match rhs {
2463 Value::Bool { .. } => Some(Ordering::Greater),
2464 Value::Int { val: rhs, .. } => compare_floats(*lhs, *rhs as f64),
2465 Value::Float { val: rhs, .. } => compare_floats(*lhs, *rhs),
2466 Value::String { .. } => Some(Ordering::Less),
2467 Value::Glob { .. } => Some(Ordering::Less),
2468 Value::Filesize { .. } => Some(Ordering::Less),
2469 Value::Duration { .. } => Some(Ordering::Less),
2470 Value::Date { .. } => Some(Ordering::Less),
2471 Value::Range { .. } => Some(Ordering::Less),
2472 Value::Record { .. } => Some(Ordering::Less),
2473 Value::List { .. } => Some(Ordering::Less),
2474 Value::Closure { .. } => Some(Ordering::Less),
2475 Value::Error { .. } => Some(Ordering::Less),
2476 Value::Binary { .. } => Some(Ordering::Less),
2477 Value::CellPath { .. } => Some(Ordering::Less),
2478 Value::Custom { .. } => Some(Ordering::Less),
2479 Value::Nothing { .. } => Some(Ordering::Less),
2480 },
2481 (Value::String { val: lhs, .. }, rhs) => match rhs {
2482 Value::Bool { .. } => Some(Ordering::Greater),
2483 Value::Int { .. } => Some(Ordering::Greater),
2484 Value::Float { .. } => Some(Ordering::Greater),
2485 Value::String { val: rhs, .. } => lhs.partial_cmp(rhs),
2486 Value::Glob { val: rhs, .. } => lhs.partial_cmp(rhs),
2487 Value::Filesize { .. } => Some(Ordering::Less),
2488 Value::Duration { .. } => Some(Ordering::Less),
2489 Value::Date { .. } => Some(Ordering::Less),
2490 Value::Range { .. } => Some(Ordering::Less),
2491 Value::Record { .. } => Some(Ordering::Less),
2492 Value::List { .. } => Some(Ordering::Less),
2493 Value::Closure { .. } => Some(Ordering::Less),
2494 Value::Error { .. } => Some(Ordering::Less),
2495 Value::Binary { .. } => Some(Ordering::Less),
2496 Value::CellPath { .. } => Some(Ordering::Less),
2497 Value::Custom { .. } => Some(Ordering::Less),
2498 Value::Nothing { .. } => Some(Ordering::Less),
2499 },
2500 (Value::Glob { val: lhs, .. }, rhs) => match rhs {
2501 Value::Bool { .. } => Some(Ordering::Greater),
2502 Value::Int { .. } => Some(Ordering::Greater),
2503 Value::Float { .. } => Some(Ordering::Greater),
2504 Value::String { val: rhs, .. } => lhs.partial_cmp(rhs),
2505 Value::Glob { val: rhs, .. } => lhs.partial_cmp(rhs),
2506 Value::Filesize { .. } => Some(Ordering::Less),
2507 Value::Duration { .. } => Some(Ordering::Less),
2508 Value::Date { .. } => Some(Ordering::Less),
2509 Value::Range { .. } => Some(Ordering::Less),
2510 Value::Record { .. } => Some(Ordering::Less),
2511 Value::List { .. } => Some(Ordering::Less),
2512 Value::Closure { .. } => Some(Ordering::Less),
2513 Value::Error { .. } => Some(Ordering::Less),
2514 Value::Binary { .. } => Some(Ordering::Less),
2515 Value::CellPath { .. } => Some(Ordering::Less),
2516 Value::Custom { .. } => Some(Ordering::Less),
2517 Value::Nothing { .. } => Some(Ordering::Less),
2518 },
2519 (Value::Filesize { val: lhs, .. }, rhs) => match rhs {
2520 Value::Bool { .. } => Some(Ordering::Greater),
2521 Value::Int { .. } => Some(Ordering::Greater),
2522 Value::Float { .. } => Some(Ordering::Greater),
2523 Value::String { .. } => Some(Ordering::Greater),
2524 Value::Glob { .. } => Some(Ordering::Greater),
2525 Value::Filesize { val: rhs, .. } => lhs.partial_cmp(rhs),
2526 Value::Duration { .. } => Some(Ordering::Less),
2527 Value::Date { .. } => Some(Ordering::Less),
2528 Value::Range { .. } => Some(Ordering::Less),
2529 Value::Record { .. } => Some(Ordering::Less),
2530 Value::List { .. } => Some(Ordering::Less),
2531 Value::Closure { .. } => Some(Ordering::Less),
2532 Value::Error { .. } => Some(Ordering::Less),
2533 Value::Binary { .. } => Some(Ordering::Less),
2534 Value::CellPath { .. } => Some(Ordering::Less),
2535 Value::Custom { .. } => Some(Ordering::Less),
2536 Value::Nothing { .. } => Some(Ordering::Less),
2537 },
2538 (Value::Duration { val: lhs, .. }, rhs) => match rhs {
2539 Value::Bool { .. } => Some(Ordering::Greater),
2540 Value::Int { .. } => Some(Ordering::Greater),
2541 Value::Float { .. } => Some(Ordering::Greater),
2542 Value::String { .. } => Some(Ordering::Greater),
2543 Value::Glob { .. } => Some(Ordering::Greater),
2544 Value::Filesize { .. } => Some(Ordering::Greater),
2545 Value::Duration { val: rhs, .. } => lhs.partial_cmp(rhs),
2546 Value::Date { .. } => Some(Ordering::Less),
2547 Value::Range { .. } => Some(Ordering::Less),
2548 Value::Record { .. } => Some(Ordering::Less),
2549 Value::List { .. } => Some(Ordering::Less),
2550 Value::Closure { .. } => Some(Ordering::Less),
2551 Value::Error { .. } => Some(Ordering::Less),
2552 Value::Binary { .. } => Some(Ordering::Less),
2553 Value::CellPath { .. } => Some(Ordering::Less),
2554 Value::Custom { .. } => Some(Ordering::Less),
2555 Value::Nothing { .. } => Some(Ordering::Less),
2556 },
2557 (Value::Date { val: lhs, .. }, rhs) => match rhs {
2558 Value::Bool { .. } => Some(Ordering::Greater),
2559 Value::Int { .. } => Some(Ordering::Greater),
2560 Value::Float { .. } => Some(Ordering::Greater),
2561 Value::String { .. } => Some(Ordering::Greater),
2562 Value::Glob { .. } => Some(Ordering::Greater),
2563 Value::Filesize { .. } => Some(Ordering::Greater),
2564 Value::Duration { .. } => Some(Ordering::Greater),
2565 Value::Date { val: rhs, .. } => lhs.partial_cmp(rhs),
2566 Value::Range { .. } => Some(Ordering::Less),
2567 Value::Record { .. } => Some(Ordering::Less),
2568 Value::List { .. } => Some(Ordering::Less),
2569 Value::Closure { .. } => Some(Ordering::Less),
2570 Value::Error { .. } => Some(Ordering::Less),
2571 Value::Binary { .. } => Some(Ordering::Less),
2572 Value::CellPath { .. } => Some(Ordering::Less),
2573 Value::Custom { .. } => Some(Ordering::Less),
2574 Value::Nothing { .. } => Some(Ordering::Less),
2575 },
2576 (Value::Range { val: lhs, .. }, rhs) => match rhs {
2577 Value::Bool { .. } => Some(Ordering::Greater),
2578 Value::Int { .. } => Some(Ordering::Greater),
2579 Value::Float { .. } => Some(Ordering::Greater),
2580 Value::String { .. } => Some(Ordering::Greater),
2581 Value::Glob { .. } => Some(Ordering::Greater),
2582 Value::Filesize { .. } => Some(Ordering::Greater),
2583 Value::Duration { .. } => Some(Ordering::Greater),
2584 Value::Date { .. } => Some(Ordering::Greater),
2585 Value::Range { val: rhs, .. } => lhs.partial_cmp(rhs),
2586 Value::Record { .. } => Some(Ordering::Less),
2587 Value::List { .. } => Some(Ordering::Less),
2588 Value::Closure { .. } => Some(Ordering::Less),
2589 Value::Error { .. } => Some(Ordering::Less),
2590 Value::Binary { .. } => Some(Ordering::Less),
2591 Value::CellPath { .. } => Some(Ordering::Less),
2592 Value::Custom { .. } => Some(Ordering::Less),
2593 Value::Nothing { .. } => Some(Ordering::Less),
2594 },
2595 (Value::Record { val: lhs, .. }, rhs) => match rhs {
2596 Value::Bool { .. } => Some(Ordering::Greater),
2597 Value::Int { .. } => Some(Ordering::Greater),
2598 Value::Float { .. } => Some(Ordering::Greater),
2599 Value::String { .. } => Some(Ordering::Greater),
2600 Value::Glob { .. } => Some(Ordering::Greater),
2601 Value::Filesize { .. } => Some(Ordering::Greater),
2602 Value::Duration { .. } => Some(Ordering::Greater),
2603 Value::Date { .. } => Some(Ordering::Greater),
2604 Value::Range { .. } => Some(Ordering::Greater),
2605 Value::Record { val: rhs, .. } => {
2606 let mut lhs = lhs.clone().into_owned();
2610 let mut rhs = rhs.clone().into_owned();
2611 lhs.sort_cols();
2612 rhs.sort_cols();
2613
2614 for (a, b) in lhs.columns().zip(rhs.columns()) {
2616 let result = a.partial_cmp(b);
2617 if result != Some(Ordering::Equal) {
2618 return result;
2619 }
2620 }
2621 for (a, b) in lhs.values().zip(rhs.values()) {
2623 let result = a.partial_cmp(b);
2624 if result != Some(Ordering::Equal) {
2625 return result;
2626 }
2627 }
2628 lhs.len().partial_cmp(&rhs.len())
2631 }
2632 Value::List { .. } => Some(Ordering::Less),
2633 Value::Closure { .. } => Some(Ordering::Less),
2634 Value::Error { .. } => Some(Ordering::Less),
2635 Value::Binary { .. } => Some(Ordering::Less),
2636 Value::CellPath { .. } => Some(Ordering::Less),
2637 Value::Custom { .. } => Some(Ordering::Less),
2638 Value::Nothing { .. } => Some(Ordering::Less),
2639 },
2640 (Value::List { vals: lhs, .. }, rhs) => match rhs {
2641 Value::Bool { .. } => Some(Ordering::Greater),
2642 Value::Int { .. } => Some(Ordering::Greater),
2643 Value::Float { .. } => Some(Ordering::Greater),
2644 Value::String { .. } => Some(Ordering::Greater),
2645 Value::Glob { .. } => Some(Ordering::Greater),
2646 Value::Filesize { .. } => Some(Ordering::Greater),
2647 Value::Duration { .. } => Some(Ordering::Greater),
2648 Value::Date { .. } => Some(Ordering::Greater),
2649 Value::Range { .. } => Some(Ordering::Greater),
2650 Value::Record { .. } => Some(Ordering::Greater),
2651 Value::List { vals: rhs, .. } => lhs.partial_cmp(rhs),
2652 Value::Closure { .. } => Some(Ordering::Less),
2653 Value::Error { .. } => Some(Ordering::Less),
2654 Value::Binary { .. } => Some(Ordering::Less),
2655 Value::CellPath { .. } => Some(Ordering::Less),
2656 Value::Custom { .. } => Some(Ordering::Less),
2657 Value::Nothing { .. } => Some(Ordering::Less),
2658 },
2659 (Value::Closure { val: lhs, .. }, rhs) => match rhs {
2660 Value::Bool { .. } => Some(Ordering::Greater),
2661 Value::Int { .. } => Some(Ordering::Greater),
2662 Value::Float { .. } => Some(Ordering::Greater),
2663 Value::String { .. } => Some(Ordering::Greater),
2664 Value::Glob { .. } => Some(Ordering::Greater),
2665 Value::Filesize { .. } => Some(Ordering::Greater),
2666 Value::Duration { .. } => Some(Ordering::Greater),
2667 Value::Date { .. } => Some(Ordering::Greater),
2668 Value::Range { .. } => Some(Ordering::Greater),
2669 Value::Record { .. } => Some(Ordering::Greater),
2670 Value::List { .. } => Some(Ordering::Greater),
2671 Value::Closure { val: rhs, .. } => lhs.block_id.partial_cmp(&rhs.block_id),
2672 Value::Error { .. } => Some(Ordering::Less),
2673 Value::Binary { .. } => Some(Ordering::Less),
2674 Value::CellPath { .. } => Some(Ordering::Less),
2675 Value::Custom { .. } => Some(Ordering::Less),
2676 Value::Nothing { .. } => Some(Ordering::Less),
2677 },
2678 (Value::Error { .. }, rhs) => match rhs {
2679 Value::Bool { .. } => Some(Ordering::Greater),
2680 Value::Int { .. } => Some(Ordering::Greater),
2681 Value::Float { .. } => Some(Ordering::Greater),
2682 Value::String { .. } => Some(Ordering::Greater),
2683 Value::Glob { .. } => Some(Ordering::Greater),
2684 Value::Filesize { .. } => Some(Ordering::Greater),
2685 Value::Duration { .. } => Some(Ordering::Greater),
2686 Value::Date { .. } => Some(Ordering::Greater),
2687 Value::Range { .. } => Some(Ordering::Greater),
2688 Value::Record { .. } => Some(Ordering::Greater),
2689 Value::List { .. } => Some(Ordering::Greater),
2690 Value::Closure { .. } => Some(Ordering::Greater),
2691 Value::Error { .. } => Some(Ordering::Equal),
2692 Value::Binary { .. } => Some(Ordering::Less),
2693 Value::CellPath { .. } => Some(Ordering::Less),
2694 Value::Custom { .. } => Some(Ordering::Less),
2695 Value::Nothing { .. } => Some(Ordering::Less),
2696 },
2697 (Value::Binary { val: lhs, .. }, rhs) => match rhs {
2698 Value::Bool { .. } => Some(Ordering::Greater),
2699 Value::Int { .. } => Some(Ordering::Greater),
2700 Value::Float { .. } => Some(Ordering::Greater),
2701 Value::String { .. } => Some(Ordering::Greater),
2702 Value::Glob { .. } => Some(Ordering::Greater),
2703 Value::Filesize { .. } => Some(Ordering::Greater),
2704 Value::Duration { .. } => Some(Ordering::Greater),
2705 Value::Date { .. } => Some(Ordering::Greater),
2706 Value::Range { .. } => Some(Ordering::Greater),
2707 Value::Record { .. } => Some(Ordering::Greater),
2708 Value::List { .. } => Some(Ordering::Greater),
2709 Value::Closure { .. } => Some(Ordering::Greater),
2710 Value::Error { .. } => Some(Ordering::Greater),
2711 Value::Binary { val: rhs, .. } => lhs.partial_cmp(rhs),
2712 Value::CellPath { .. } => Some(Ordering::Less),
2713 Value::Custom { .. } => Some(Ordering::Less),
2714 Value::Nothing { .. } => Some(Ordering::Less),
2715 },
2716 (Value::CellPath { val: lhs, .. }, rhs) => match rhs {
2717 Value::Bool { .. } => Some(Ordering::Greater),
2718 Value::Int { .. } => Some(Ordering::Greater),
2719 Value::Float { .. } => Some(Ordering::Greater),
2720 Value::String { .. } => Some(Ordering::Greater),
2721 Value::Glob { .. } => Some(Ordering::Greater),
2722 Value::Filesize { .. } => Some(Ordering::Greater),
2723 Value::Duration { .. } => Some(Ordering::Greater),
2724 Value::Date { .. } => Some(Ordering::Greater),
2725 Value::Range { .. } => Some(Ordering::Greater),
2726 Value::Record { .. } => Some(Ordering::Greater),
2727 Value::List { .. } => Some(Ordering::Greater),
2728 Value::Closure { .. } => Some(Ordering::Greater),
2729 Value::Error { .. } => Some(Ordering::Greater),
2730 Value::Binary { .. } => Some(Ordering::Greater),
2731 Value::CellPath { val: rhs, .. } => lhs.partial_cmp(rhs),
2732 Value::Custom { .. } => Some(Ordering::Less),
2733 Value::Nothing { .. } => Some(Ordering::Less),
2734 },
2735 (Value::Custom { val: lhs, .. }, rhs) => lhs.partial_cmp(rhs),
2736 (Value::Nothing { .. }, rhs) => match rhs {
2737 Value::Bool { .. } => Some(Ordering::Greater),
2738 Value::Int { .. } => Some(Ordering::Greater),
2739 Value::Float { .. } => Some(Ordering::Greater),
2740 Value::String { .. } => Some(Ordering::Greater),
2741 Value::Glob { .. } => Some(Ordering::Greater),
2742 Value::Filesize { .. } => Some(Ordering::Greater),
2743 Value::Duration { .. } => Some(Ordering::Greater),
2744 Value::Date { .. } => Some(Ordering::Greater),
2745 Value::Range { .. } => Some(Ordering::Greater),
2746 Value::Record { .. } => Some(Ordering::Greater),
2747 Value::List { .. } => Some(Ordering::Greater),
2748 Value::Closure { .. } => Some(Ordering::Greater),
2749 Value::Error { .. } => Some(Ordering::Greater),
2750 Value::Binary { .. } => Some(Ordering::Greater),
2751 Value::CellPath { .. } => Some(Ordering::Greater),
2752 Value::Custom { .. } => Some(Ordering::Greater),
2753 Value::Nothing { .. } => Some(Ordering::Equal),
2754 },
2755 }
2756 }
2757}
2758
2759impl PartialEq for Value {
2760 fn eq(&self, other: &Self) -> bool {
2761 self.partial_cmp(other).is_some_and(Ordering::is_eq)
2762 }
2763}
2764
2765fn checked_duration_operation<F>(a: i64, b: i64, op: F, span: Span) -> Result<Value, ShellError>
2766where
2767 F: Fn(i64, i64) -> Option<i64>,
2768{
2769 if let Some(val) = op(a, b) {
2770 Ok(Value::duration(val, span))
2771 } else {
2772 Err(ShellError::OperatorOverflow {
2773 msg: "operation overflowed".to_owned(),
2774 span,
2775 help: None,
2776 })
2777 }
2778}
2779
2780impl Value {
2781 pub fn add(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
2782 match (self, rhs) {
2783 (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2784 if let Some(val) = lhs.checked_add(*rhs) {
2785 Ok(Value::int(val, span))
2786 } else {
2787 Err(ShellError::OperatorOverflow {
2788 msg: "add operation overflowed".into(),
2789 span,
2790 help: Some("Consider using floating point values for increased range by promoting operand with 'into float'. Note: float has reduced precision!".into()),
2791 })
2792 }
2793 }
2794 (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2795 Ok(Value::float(*lhs as f64 + *rhs, span))
2796 }
2797 (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2798 Ok(Value::float(*lhs + *rhs as f64, span))
2799 }
2800 (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2801 Ok(Value::float(lhs + rhs, span))
2802 }
2803 (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
2804 Ok(Value::string(lhs.to_string() + rhs, span))
2805 }
2806 (Value::Duration { val: lhs, .. }, Value::Date { val: rhs, .. }) => {
2807 if let Some(val) = rhs.checked_add_signed(chrono::Duration::nanoseconds(*lhs)) {
2808 Ok(Value::date(val, span))
2809 } else {
2810 Err(ShellError::OperatorOverflow {
2811 msg: "addition operation overflowed".into(),
2812 span,
2813 help: None,
2814 })
2815 }
2816 }
2817 (Value::Date { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
2818 if let Some(val) = lhs.checked_add_signed(chrono::Duration::nanoseconds(*rhs)) {
2819 Ok(Value::date(val, span))
2820 } else {
2821 Err(ShellError::OperatorOverflow {
2822 msg: "addition operation overflowed".into(),
2823 span,
2824 help: None,
2825 })
2826 }
2827 }
2828 (Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
2829 checked_duration_operation(*lhs, *rhs, i64::checked_add, span)
2830 }
2831 (Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
2832 if let Some(val) = *lhs + *rhs {
2833 Ok(Value::filesize(val, span))
2834 } else {
2835 Err(ShellError::OperatorOverflow {
2836 msg: "add operation overflowed".into(),
2837 span,
2838 help: None,
2839 })
2840 }
2841 }
2842 (Value::Custom { val: lhs, .. }, rhs) => {
2843 lhs.operation(self.span(), Operator::Math(Math::Add), op, rhs)
2844 }
2845 _ => Err(operator_type_error(
2846 Operator::Math(Math::Add),
2847 op,
2848 self,
2849 rhs,
2850 |val| {
2851 matches!(
2852 val,
2853 Value::Int { .. }
2854 | Value::Float { .. }
2855 | Value::String { .. }
2856 | Value::Date { .. }
2857 | Value::Duration { .. }
2858 | Value::Filesize { .. },
2859 )
2860 },
2861 )),
2862 }
2863 }
2864
2865 pub fn sub(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
2866 match (self, rhs) {
2867 (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2868 if let Some(val) = lhs.checked_sub(*rhs) {
2869 Ok(Value::int(val, span))
2870 } else {
2871 Err(ShellError::OperatorOverflow {
2872 msg: "subtraction operation overflowed".into(),
2873 span,
2874 help: Some("Consider using floating point values for increased range by promoting operand with 'into float'. Note: float has reduced precision!".into()),
2875 })
2876 }
2877 }
2878 (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2879 Ok(Value::float(*lhs as f64 - *rhs, span))
2880 }
2881 (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2882 Ok(Value::float(*lhs - *rhs as f64, span))
2883 }
2884 (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2885 Ok(Value::float(lhs - rhs, span))
2886 }
2887 (Value::Date { val: lhs, .. }, Value::Date { val: rhs, .. }) => {
2888 let result = lhs.signed_duration_since(*rhs);
2889 if let Some(v) = result.num_nanoseconds() {
2890 Ok(Value::duration(v, span))
2891 } else {
2892 Err(ShellError::OperatorOverflow {
2893 msg: "subtraction operation overflowed".into(),
2894 span,
2895 help: None,
2896 })
2897 }
2898 }
2899 (Value::Date { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
2900 match lhs.checked_sub_signed(chrono::Duration::nanoseconds(*rhs)) {
2901 Some(val) => Ok(Value::date(val, span)),
2902 _ => Err(ShellError::OperatorOverflow {
2903 msg: "subtraction operation overflowed".into(),
2904 span,
2905 help: None,
2906 }),
2907 }
2908 }
2909 (Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
2910 checked_duration_operation(*lhs, *rhs, i64::checked_sub, span)
2911 }
2912 (Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
2913 if let Some(val) = *lhs - *rhs {
2914 Ok(Value::filesize(val, span))
2915 } else {
2916 Err(ShellError::OperatorOverflow {
2917 msg: "add operation overflowed".into(),
2918 span,
2919 help: None,
2920 })
2921 }
2922 }
2923 (Value::Custom { val: lhs, .. }, rhs) => {
2924 lhs.operation(self.span(), Operator::Math(Math::Subtract), op, rhs)
2925 }
2926 _ => Err(operator_type_error(
2927 Operator::Math(Math::Subtract),
2928 op,
2929 self,
2930 rhs,
2931 |val| {
2932 matches!(
2933 val,
2934 Value::Int { .. }
2935 | Value::Float { .. }
2936 | Value::Date { .. }
2937 | Value::Duration { .. }
2938 | Value::Filesize { .. },
2939 )
2940 },
2941 )),
2942 }
2943 }
2944
2945 pub fn mul(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
2946 match (self, rhs) {
2947 (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2948 if let Some(val) = lhs.checked_mul(*rhs) {
2949 Ok(Value::int(val, span))
2950 } else {
2951 Err(ShellError::OperatorOverflow {
2952 msg: "multiply operation overflowed".into(),
2953 span,
2954 help: Some("Consider using floating point values for increased range by promoting operand with 'into float'. Note: float has reduced precision!".into()),
2955 })
2956 }
2957 }
2958 (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2959 Ok(Value::float(*lhs as f64 * *rhs, span))
2960 }
2961 (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2962 Ok(Value::float(*lhs * *rhs as f64, span))
2963 }
2964 (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
2965 Ok(Value::float(lhs * rhs, span))
2966 }
2967 (Value::Int { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
2968 if let Some(val) = *lhs * *rhs {
2969 Ok(Value::filesize(val, span))
2970 } else {
2971 Err(ShellError::OperatorOverflow {
2972 msg: "multiply operation overflowed".into(),
2973 span,
2974 help: None,
2975 })
2976 }
2977 }
2978 (Value::Filesize { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
2979 if let Some(val) = *lhs * *rhs {
2980 Ok(Value::filesize(val, span))
2981 } else {
2982 Err(ShellError::OperatorOverflow {
2983 msg: "multiply operation overflowed".into(),
2984 span,
2985 help: None,
2986 })
2987 }
2988 }
2989 (Value::Float { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
2990 if let Some(val) = *lhs * *rhs {
2991 Ok(Value::filesize(val, span))
2992 } else {
2993 Err(ShellError::OperatorOverflow {
2994 msg: "multiply operation overflowed".into(),
2995 span,
2996 help: None,
2997 })
2998 }
2999 }
3000 (Value::Filesize { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3001 if let Some(val) = *lhs * *rhs {
3002 Ok(Value::filesize(val, span))
3003 } else {
3004 Err(ShellError::OperatorOverflow {
3005 msg: "multiply operation overflowed".into(),
3006 span,
3007 help: None,
3008 })
3009 }
3010 }
3011 (Value::Int { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
3012 checked_duration_operation(*lhs, *rhs, i64::checked_mul, span)
3013 }
3014 (Value::Duration { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3015 checked_duration_operation(*lhs, *rhs, i64::checked_mul, span)
3016 }
3017 (Value::Duration { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3018 Ok(Value::duration((*lhs as f64 * *rhs) as i64, span))
3019 }
3020 (Value::Float { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
3021 Ok(Value::duration((*lhs * *rhs as f64) as i64, span))
3022 }
3023 (Value::Custom { val: lhs, .. }, rhs) => {
3024 lhs.operation(self.span(), Operator::Math(Math::Multiply), op, rhs)
3025 }
3026 _ => Err(operator_type_error(
3027 Operator::Math(Math::Multiply),
3028 op,
3029 self,
3030 rhs,
3031 |val| {
3032 matches!(
3033 val,
3034 Value::Int { .. }
3035 | Value::Float { .. }
3036 | Value::Duration { .. }
3037 | Value::Filesize { .. },
3038 )
3039 },
3040 )),
3041 }
3042 }
3043
3044 pub fn div(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3045 match (self, rhs) {
3046 (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3047 if *rhs == 0 {
3048 Err(ShellError::DivisionByZero { span: op })
3049 } else {
3050 Ok(Value::float(*lhs as f64 / *rhs as f64, span))
3051 }
3052 }
3053 (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3054 if *rhs != 0.0 {
3055 Ok(Value::float(*lhs as f64 / *rhs, span))
3056 } else {
3057 Err(ShellError::DivisionByZero { span: op })
3058 }
3059 }
3060 (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3061 if *rhs != 0 {
3062 Ok(Value::float(*lhs / *rhs as f64, span))
3063 } else {
3064 Err(ShellError::DivisionByZero { span: op })
3065 }
3066 }
3067 (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3068 if *rhs != 0.0 {
3069 Ok(Value::float(lhs / rhs, span))
3070 } else {
3071 Err(ShellError::DivisionByZero { span: op })
3072 }
3073 }
3074 (Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
3075 if *rhs == Filesize::ZERO {
3076 Err(ShellError::DivisionByZero { span: op })
3077 } else {
3078 Ok(Value::float(lhs.get() as f64 / rhs.get() as f64, span))
3079 }
3080 }
3081 (Value::Filesize { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3082 if let Some(val) = lhs.get().checked_div(*rhs) {
3083 Ok(Value::filesize(val, span))
3084 } else if *rhs == 0 {
3085 Err(ShellError::DivisionByZero { span: op })
3086 } else {
3087 Err(ShellError::OperatorOverflow {
3088 msg: "division operation overflowed".into(),
3089 span,
3090 help: None,
3091 })
3092 }
3093 }
3094 (Value::Filesize { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3095 if *rhs != 0.0 {
3096 if let Ok(val) = Filesize::try_from(lhs.get() as f64 / rhs) {
3097 Ok(Value::filesize(val, span))
3098 } else {
3099 Err(ShellError::OperatorOverflow {
3100 msg: "division operation overflowed".into(),
3101 span,
3102 help: None,
3103 })
3104 }
3105 } else {
3106 Err(ShellError::DivisionByZero { span: op })
3107 }
3108 }
3109 (Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
3110 if *rhs == 0 {
3111 Err(ShellError::DivisionByZero { span: op })
3112 } else {
3113 Ok(Value::float(*lhs as f64 / *rhs as f64, span))
3114 }
3115 }
3116 (Value::Duration { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3117 if let Some(val) = lhs.checked_div(*rhs) {
3118 Ok(Value::duration(val, span))
3119 } else if *rhs == 0 {
3120 Err(ShellError::DivisionByZero { span: op })
3121 } else {
3122 Err(ShellError::OperatorOverflow {
3123 msg: "division operation overflowed".into(),
3124 span,
3125 help: None,
3126 })
3127 }
3128 }
3129 (Value::Duration { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3130 if *rhs != 0.0 {
3131 let val = *lhs as f64 / rhs;
3132 if i64::MIN as f64 <= val && val <= i64::MAX as f64 {
3133 Ok(Value::duration(val as i64, span))
3134 } else {
3135 Err(ShellError::OperatorOverflow {
3136 msg: "division operation overflowed".into(),
3137 span,
3138 help: None,
3139 })
3140 }
3141 } else {
3142 Err(ShellError::DivisionByZero { span: op })
3143 }
3144 }
3145 (Value::Custom { val: lhs, .. }, rhs) => {
3146 lhs.operation(self.span(), Operator::Math(Math::Divide), op, rhs)
3147 }
3148 _ => Err(operator_type_error(
3149 Operator::Math(Math::Divide),
3150 op,
3151 self,
3152 rhs,
3153 |val| {
3154 matches!(
3155 val,
3156 Value::Int { .. }
3157 | Value::Float { .. }
3158 | Value::Duration { .. }
3159 | Value::Filesize { .. },
3160 )
3161 },
3162 )),
3163 }
3164 }
3165
3166 pub fn floor_div(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3167 fn checked_div_floor_i64(dividend: i64, divisor: i64) -> Option<i64> {
3169 let quotient = dividend.checked_div(divisor)?;
3170 let remainder = dividend.checked_rem(divisor)?;
3171 if (remainder > 0 && divisor < 0) || (remainder < 0 && divisor > 0) {
3172 Some(quotient - 1)
3178 } else {
3179 Some(quotient)
3180 }
3181 }
3182
3183 fn checked_div_floor_f64(dividend: f64, divisor: f64) -> Option<f64> {
3184 if divisor == 0.0 {
3185 None
3186 } else {
3187 Some((dividend / divisor).floor())
3188 }
3189 }
3190
3191 match (self, rhs) {
3192 (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3193 if let Some(val) = checked_div_floor_i64(*lhs, *rhs) {
3194 Ok(Value::int(val, span))
3195 } else if *rhs == 0 {
3196 Err(ShellError::DivisionByZero { span: op })
3197 } else {
3198 Err(ShellError::OperatorOverflow {
3199 msg: "division operation overflowed".into(),
3200 span,
3201 help: None,
3202 })
3203 }
3204 }
3205 (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3206 if let Some(val) = checked_div_floor_f64(*lhs as f64, *rhs) {
3207 Ok(Value::float(val, span))
3208 } else {
3209 Err(ShellError::DivisionByZero { span: op })
3210 }
3211 }
3212 (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3213 if let Some(val) = checked_div_floor_f64(*lhs, *rhs as f64) {
3214 Ok(Value::float(val, span))
3215 } else {
3216 Err(ShellError::DivisionByZero { span: op })
3217 }
3218 }
3219 (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3220 if let Some(val) = checked_div_floor_f64(*lhs, *rhs) {
3221 Ok(Value::float(val, span))
3222 } else {
3223 Err(ShellError::DivisionByZero { span: op })
3224 }
3225 }
3226 (Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
3227 if let Some(val) = checked_div_floor_i64(lhs.get(), rhs.get()) {
3228 Ok(Value::int(val, span))
3229 } else if *rhs == Filesize::ZERO {
3230 Err(ShellError::DivisionByZero { span: op })
3231 } else {
3232 Err(ShellError::OperatorOverflow {
3233 msg: "division operation overflowed".into(),
3234 span,
3235 help: None,
3236 })
3237 }
3238 }
3239 (Value::Filesize { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3240 if let Some(val) = checked_div_floor_i64(lhs.get(), *rhs) {
3241 Ok(Value::filesize(val, span))
3242 } else if *rhs == 0 {
3243 Err(ShellError::DivisionByZero { span: op })
3244 } else {
3245 Err(ShellError::OperatorOverflow {
3246 msg: "division operation overflowed".into(),
3247 span,
3248 help: None,
3249 })
3250 }
3251 }
3252 (Value::Filesize { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3253 if let Some(val) = checked_div_floor_f64(lhs.get() as f64, *rhs) {
3254 if let Ok(val) = Filesize::try_from(val) {
3255 Ok(Value::filesize(val, span))
3256 } else {
3257 Err(ShellError::OperatorOverflow {
3258 msg: "division operation overflowed".into(),
3259 span,
3260 help: None,
3261 })
3262 }
3263 } else {
3264 Err(ShellError::DivisionByZero { span: op })
3265 }
3266 }
3267 (Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
3268 if let Some(val) = checked_div_floor_i64(*lhs, *rhs) {
3269 Ok(Value::int(val, span))
3270 } else if *rhs == 0 {
3271 Err(ShellError::DivisionByZero { span: op })
3272 } else {
3273 Err(ShellError::OperatorOverflow {
3274 msg: "division operation overflowed".into(),
3275 span,
3276 help: None,
3277 })
3278 }
3279 }
3280 (Value::Duration { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3281 if let Some(val) = checked_div_floor_i64(*lhs, *rhs) {
3282 Ok(Value::duration(val, span))
3283 } else if *rhs == 0 {
3284 Err(ShellError::DivisionByZero { span: op })
3285 } else {
3286 Err(ShellError::OperatorOverflow {
3287 msg: "division operation overflowed".into(),
3288 span,
3289 help: None,
3290 })
3291 }
3292 }
3293 (Value::Duration { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3294 if let Some(val) = checked_div_floor_f64(*lhs as f64, *rhs) {
3295 if i64::MIN as f64 <= val && val <= i64::MAX as f64 {
3296 Ok(Value::duration(val as i64, span))
3297 } else {
3298 Err(ShellError::OperatorOverflow {
3299 msg: "division operation overflowed".into(),
3300 span,
3301 help: None,
3302 })
3303 }
3304 } else {
3305 Err(ShellError::DivisionByZero { span: op })
3306 }
3307 }
3308 (Value::Custom { val: lhs, .. }, rhs) => {
3309 lhs.operation(self.span(), Operator::Math(Math::FloorDivide), op, rhs)
3310 }
3311 _ => Err(operator_type_error(
3312 Operator::Math(Math::FloorDivide),
3313 op,
3314 self,
3315 rhs,
3316 |val| {
3317 matches!(
3318 val,
3319 Value::Int { .. }
3320 | Value::Float { .. }
3321 | Value::Duration { .. }
3322 | Value::Filesize { .. },
3323 )
3324 },
3325 )),
3326 }
3327 }
3328
3329 pub fn modulo(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3330 fn checked_mod_i64(dividend: i64, divisor: i64) -> Option<i64> {
3332 let remainder = dividend.checked_rem(divisor)?;
3333 if (remainder > 0 && divisor < 0) || (remainder < 0 && divisor > 0) {
3334 Some(remainder + divisor)
3337 } else {
3338 Some(remainder)
3339 }
3340 }
3341
3342 fn checked_mod_f64(dividend: f64, divisor: f64) -> Option<f64> {
3343 if divisor == 0.0 {
3344 None
3345 } else {
3346 let remainder = dividend % divisor;
3347 if (remainder > 0.0 && divisor < 0.0) || (remainder < 0.0 && divisor > 0.0) {
3348 Some(remainder + divisor)
3349 } else {
3350 Some(remainder)
3351 }
3352 }
3353 }
3354
3355 match (self, rhs) {
3356 (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3357 if let Some(val) = checked_mod_i64(*lhs, *rhs) {
3358 Ok(Value::int(val, span))
3359 } else if *rhs == 0 {
3360 Err(ShellError::DivisionByZero { span: op })
3361 } else {
3362 Err(ShellError::OperatorOverflow {
3363 msg: "modulo operation overflowed".into(),
3364 span,
3365 help: None,
3366 })
3367 }
3368 }
3369 (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3370 if let Some(val) = checked_mod_f64(*lhs as f64, *rhs) {
3371 Ok(Value::float(val, span))
3372 } else {
3373 Err(ShellError::DivisionByZero { span: op })
3374 }
3375 }
3376 (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3377 if let Some(val) = checked_mod_f64(*lhs, *rhs as f64) {
3378 Ok(Value::float(val, span))
3379 } else {
3380 Err(ShellError::DivisionByZero { span: op })
3381 }
3382 }
3383 (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3384 if let Some(val) = checked_mod_f64(*lhs, *rhs) {
3385 Ok(Value::float(val, span))
3386 } else {
3387 Err(ShellError::DivisionByZero { span: op })
3388 }
3389 }
3390 (Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
3391 if let Some(val) = checked_mod_i64(lhs.get(), rhs.get()) {
3392 Ok(Value::filesize(val, span))
3393 } else if *rhs == Filesize::ZERO {
3394 Err(ShellError::DivisionByZero { span: op })
3395 } else {
3396 Err(ShellError::OperatorOverflow {
3397 msg: "modulo operation overflowed".into(),
3398 span,
3399 help: None,
3400 })
3401 }
3402 }
3403 (Value::Filesize { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3404 if let Some(val) = checked_mod_i64(lhs.get(), *rhs) {
3405 Ok(Value::filesize(val, span))
3406 } else if *rhs == 0 {
3407 Err(ShellError::DivisionByZero { span: op })
3408 } else {
3409 Err(ShellError::OperatorOverflow {
3410 msg: "modulo operation overflowed".into(),
3411 span,
3412 help: None,
3413 })
3414 }
3415 }
3416 (Value::Filesize { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3417 if let Some(val) = checked_mod_f64(lhs.get() as f64, *rhs) {
3418 if let Ok(val) = Filesize::try_from(val) {
3419 Ok(Value::filesize(val, span))
3420 } else {
3421 Err(ShellError::OperatorOverflow {
3422 msg: "modulo operation overflowed".into(),
3423 span,
3424 help: None,
3425 })
3426 }
3427 } else {
3428 Err(ShellError::DivisionByZero { span: op })
3429 }
3430 }
3431 (Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
3432 if let Some(val) = checked_mod_i64(*lhs, *rhs) {
3433 Ok(Value::duration(val, span))
3434 } else if *rhs == 0 {
3435 Err(ShellError::DivisionByZero { span: op })
3436 } else {
3437 Err(ShellError::OperatorOverflow {
3438 msg: "division operation overflowed".into(),
3439 span,
3440 help: None,
3441 })
3442 }
3443 }
3444 (Value::Duration { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3445 if let Some(val) = checked_mod_i64(*lhs, *rhs) {
3446 Ok(Value::duration(val, span))
3447 } else if *rhs == 0 {
3448 Err(ShellError::DivisionByZero { span: op })
3449 } else {
3450 Err(ShellError::OperatorOverflow {
3451 msg: "division operation overflowed".into(),
3452 span,
3453 help: None,
3454 })
3455 }
3456 }
3457 (Value::Duration { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3458 if let Some(val) = checked_mod_f64(*lhs as f64, *rhs) {
3459 if i64::MIN as f64 <= val && val <= i64::MAX as f64 {
3460 Ok(Value::duration(val as i64, span))
3461 } else {
3462 Err(ShellError::OperatorOverflow {
3463 msg: "division operation overflowed".into(),
3464 span,
3465 help: None,
3466 })
3467 }
3468 } else {
3469 Err(ShellError::DivisionByZero { span: op })
3470 }
3471 }
3472 (Value::Custom { val: lhs, .. }, rhs) => {
3473 lhs.operation(span, Operator::Math(Math::Modulo), op, rhs)
3474 }
3475 _ => Err(operator_type_error(
3476 Operator::Math(Math::Modulo),
3477 op,
3478 self,
3479 rhs,
3480 |val| {
3481 matches!(
3482 val,
3483 Value::Int { .. }
3484 | Value::Float { .. }
3485 | Value::Duration { .. }
3486 | Value::Filesize { .. },
3487 )
3488 },
3489 )),
3490 }
3491 }
3492
3493 pub fn pow(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3494 match (self, rhs) {
3495 (Value::Int { val: lhs, .. }, Value::Int { val: rhsv, .. }) => {
3496 if *rhsv < 0 {
3497 return Err(ShellError::IncorrectValue {
3498 msg: "Negative exponent for integer power is unsupported; use floats instead.".into(),
3499 val_span: rhs.span(),
3500 call_span: op,
3501 });
3502 }
3503
3504 if let Some(val) = lhs.checked_pow(*rhsv as u32) {
3505 Ok(Value::int(val, span))
3506 } else {
3507 Err(ShellError::OperatorOverflow {
3508 msg: "pow operation overflowed".into(),
3509 span,
3510 help: Some("Consider using floating point values for increased range by promoting operand with 'into float'. Note: float has reduced precision!".into()),
3511 })
3512 }
3513 }
3514 (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3515 Ok(Value::float((*lhs as f64).powf(*rhs), span))
3516 }
3517 (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
3518 Ok(Value::float(lhs.powf(*rhs as f64), span))
3519 }
3520 (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
3521 Ok(Value::float(lhs.powf(*rhs), span))
3522 }
3523 (Value::Custom { val: lhs, .. }, rhs) => {
3524 lhs.operation(span, Operator::Math(Math::Pow), op, rhs)
3525 }
3526 _ => Err(operator_type_error(
3527 Operator::Math(Math::Pow),
3528 op,
3529 self,
3530 rhs,
3531 |val| matches!(val, Value::Int { .. } | Value::Float { .. }),
3532 )),
3533 }
3534 }
3535
3536 pub fn concat(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3537 match (self, rhs) {
3538 (Value::List { vals: lhs, .. }, Value::List { vals: rhs, .. }) => {
3539 Ok(Value::list([lhs.as_slice(), rhs.as_slice()].concat(), span))
3540 }
3541 (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
3542 Ok(Value::string([lhs.as_str(), rhs.as_str()].join(""), span))
3543 }
3544 (Value::Binary { val: lhs, .. }, Value::Binary { val: rhs, .. }) => Ok(Value::binary(
3545 [lhs.as_slice(), rhs.as_slice()].concat(),
3546 span,
3547 )),
3548 (Value::Custom { val: lhs, .. }, rhs) => {
3549 lhs.operation(self.span(), Operator::Math(Math::Concatenate), op, rhs)
3550 }
3551 _ => {
3552 let help = if matches!(self, Value::List { .. })
3553 || matches!(rhs, Value::List { .. })
3554 {
3555 Some(
3556 "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`.",
3557 )
3558 } else {
3559 None
3560 };
3561 let is_supported = |val: &Value| {
3562 matches!(
3563 val,
3564 Value::List { .. }
3565 | Value::String { .. }
3566 | Value::Binary { .. }
3567 | Value::Custom { .. }
3568 )
3569 };
3570 Err(match (is_supported(self), is_supported(rhs)) {
3571 (true, true) => ShellError::OperatorIncompatibleTypes {
3572 op: Operator::Math(Math::Concatenate),
3573 lhs: self.get_type(),
3574 rhs: rhs.get_type(),
3575 op_span: op,
3576 lhs_span: self.span(),
3577 rhs_span: rhs.span(),
3578 help,
3579 },
3580 (true, false) => ShellError::OperatorUnsupportedType {
3581 op: Operator::Math(Math::Concatenate),
3582 unsupported: rhs.get_type(),
3583 op_span: op,
3584 unsupported_span: rhs.span(),
3585 help,
3586 },
3587 (false, _) => ShellError::OperatorUnsupportedType {
3588 op: Operator::Math(Math::Concatenate),
3589 unsupported: self.get_type(),
3590 op_span: op,
3591 unsupported_span: self.span(),
3592 help,
3593 },
3594 })
3595 }
3596 }
3597 }
3598
3599 pub fn lt(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3600 if let (Value::Custom { val: lhs, .. }, rhs) = (self, rhs) {
3601 return lhs.operation(
3602 self.span(),
3603 Operator::Comparison(Comparison::LessThan),
3604 op,
3605 rhs,
3606 );
3607 }
3608
3609 if matches!(self, Value::Nothing { .. }) || matches!(rhs, Value::Nothing { .. }) {
3610 return Ok(Value::nothing(span));
3611 }
3612
3613 if !type_compatible(self.get_type(), rhs.get_type()) {
3614 return Err(operator_type_error(
3615 Operator::Comparison(Comparison::LessThan),
3616 op,
3617 self,
3618 rhs,
3619 |val| {
3620 matches!(
3621 val,
3622 Value::Int { .. }
3623 | Value::Float { .. }
3624 | Value::String { .. }
3625 | Value::Filesize { .. }
3626 | Value::Duration { .. }
3627 | Value::Date { .. }
3628 | Value::Bool { .. }
3629 | Value::Nothing { .. }
3630 )
3631 },
3632 ));
3633 }
3634
3635 Ok(Value::bool(
3636 matches!(self.partial_cmp(rhs), Some(Ordering::Less)),
3637 span,
3638 ))
3639 }
3640
3641 pub fn lte(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3642 if let (Value::Custom { val: lhs, .. }, rhs) = (self, rhs) {
3643 return lhs.operation(
3644 self.span(),
3645 Operator::Comparison(Comparison::LessThanOrEqual),
3646 op,
3647 rhs,
3648 );
3649 }
3650
3651 if matches!(self, Value::Nothing { .. }) || matches!(rhs, Value::Nothing { .. }) {
3652 return Ok(Value::nothing(span));
3653 }
3654
3655 if !type_compatible(self.get_type(), rhs.get_type()) {
3656 return Err(operator_type_error(
3657 Operator::Comparison(Comparison::LessThanOrEqual),
3658 op,
3659 self,
3660 rhs,
3661 |val| {
3662 matches!(
3663 val,
3664 Value::Int { .. }
3665 | Value::Float { .. }
3666 | Value::String { .. }
3667 | Value::Filesize { .. }
3668 | Value::Duration { .. }
3669 | Value::Date { .. }
3670 | Value::Bool { .. }
3671 | Value::Nothing { .. }
3672 )
3673 },
3674 ));
3675 }
3676
3677 Ok(Value::bool(
3678 matches!(
3679 self.partial_cmp(rhs),
3680 Some(Ordering::Less | Ordering::Equal)
3681 ),
3682 span,
3683 ))
3684 }
3685
3686 pub fn gt(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3687 if let (Value::Custom { val: lhs, .. }, rhs) = (self, rhs) {
3688 return lhs.operation(
3689 self.span(),
3690 Operator::Comparison(Comparison::GreaterThan),
3691 op,
3692 rhs,
3693 );
3694 }
3695
3696 if matches!(self, Value::Nothing { .. }) || matches!(rhs, Value::Nothing { .. }) {
3697 return Ok(Value::nothing(span));
3698 }
3699
3700 if !type_compatible(self.get_type(), rhs.get_type()) {
3701 return Err(operator_type_error(
3702 Operator::Comparison(Comparison::GreaterThan),
3703 op,
3704 self,
3705 rhs,
3706 |val| {
3707 matches!(
3708 val,
3709 Value::Int { .. }
3710 | Value::Float { .. }
3711 | Value::String { .. }
3712 | Value::Filesize { .. }
3713 | Value::Duration { .. }
3714 | Value::Date { .. }
3715 | Value::Bool { .. }
3716 | Value::Nothing { .. }
3717 )
3718 },
3719 ));
3720 }
3721
3722 Ok(Value::bool(
3723 matches!(self.partial_cmp(rhs), Some(Ordering::Greater)),
3724 span,
3725 ))
3726 }
3727
3728 pub fn gte(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3729 if let (Value::Custom { val: lhs, .. }, rhs) = (self, rhs) {
3730 return lhs.operation(
3731 self.span(),
3732 Operator::Comparison(Comparison::GreaterThanOrEqual),
3733 op,
3734 rhs,
3735 );
3736 }
3737
3738 if matches!(self, Value::Nothing { .. }) || matches!(rhs, Value::Nothing { .. }) {
3739 return Ok(Value::nothing(span));
3740 }
3741
3742 if !type_compatible(self.get_type(), rhs.get_type()) {
3743 return Err(operator_type_error(
3744 Operator::Comparison(Comparison::GreaterThanOrEqual),
3745 op,
3746 self,
3747 rhs,
3748 |val| {
3749 matches!(
3750 val,
3751 Value::Int { .. }
3752 | Value::Float { .. }
3753 | Value::String { .. }
3754 | Value::Filesize { .. }
3755 | Value::Duration { .. }
3756 | Value::Date { .. }
3757 | Value::Bool { .. }
3758 | Value::Nothing { .. }
3759 )
3760 },
3761 ));
3762 }
3763
3764 Ok(Value::bool(
3765 matches!(
3766 self.partial_cmp(rhs),
3767 Some(Ordering::Greater | Ordering::Equal)
3768 ),
3769 span,
3770 ))
3771 }
3772
3773 pub fn eq(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3774 if let (Value::Custom { val: lhs, .. }, rhs) = (self, rhs) {
3775 return lhs.operation(
3776 self.span(),
3777 Operator::Comparison(Comparison::Equal),
3778 op,
3779 rhs,
3780 );
3781 }
3782
3783 Ok(Value::bool(
3784 matches!(self.partial_cmp(rhs), Some(Ordering::Equal)),
3785 span,
3786 ))
3787 }
3788
3789 pub fn ne(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3790 if let (Value::Custom { val: lhs, .. }, rhs) = (self, rhs) {
3791 return lhs.operation(
3792 self.span(),
3793 Operator::Comparison(Comparison::NotEqual),
3794 op,
3795 rhs,
3796 );
3797 }
3798
3799 Ok(Value::bool(
3800 !matches!(self.partial_cmp(rhs), Some(Ordering::Equal)),
3801 span,
3802 ))
3803 }
3804
3805 pub fn r#in(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3806 match (self, rhs) {
3807 (lhs, Value::Range { val: rhs, .. }) => Ok(Value::bool(rhs.contains(lhs), span)),
3808 (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
3809 Ok(Value::bool(rhs.contains(lhs), span))
3810 }
3811 (lhs, Value::List { vals: rhs, .. }) => Ok(Value::bool(rhs.contains(lhs), span)),
3812 (Value::String { val: lhs, .. }, Value::Record { val: rhs, .. }) => {
3813 Ok(Value::bool(rhs.contains(lhs), span))
3814 }
3815 (Value::String { .. } | Value::Int { .. }, Value::CellPath { val: rhs, .. }) => {
3816 let val = rhs.members.iter().any(|member| match (self, member) {
3817 (Value::Int { val: lhs, .. }, PathMember::Int { val: rhs, .. }) => {
3818 *lhs == *rhs as i64
3819 }
3820 (Value::String { val: lhs, .. }, PathMember::String { val: rhs, .. }) => {
3821 lhs == rhs
3822 }
3823 (Value::String { .. }, PathMember::Int { .. })
3824 | (Value::Int { .. }, PathMember::String { .. }) => false,
3825 _ => unreachable!(
3826 "outer match arm ensures `self` is either a `String` or `Int` variant"
3827 ),
3828 });
3829
3830 Ok(Value::bool(val, span))
3831 }
3832 (Value::CellPath { val: lhs, .. }, Value::CellPath { val: rhs, .. }) => {
3833 Ok(Value::bool(
3834 rhs.members
3835 .windows(lhs.members.len())
3836 .any(|member_window| member_window == rhs.members),
3837 span,
3838 ))
3839 }
3840 (Value::Custom { val: lhs, .. }, rhs) => {
3841 lhs.operation(self.span(), Operator::Comparison(Comparison::In), op, rhs)
3842 }
3843 (lhs, rhs) => Err(
3844 if let Value::List { .. }
3845 | Value::Range { .. }
3846 | Value::String { .. }
3847 | Value::Record { .. }
3848 | Value::Custom { .. } = rhs
3849 {
3850 ShellError::OperatorIncompatibleTypes {
3851 op: Operator::Comparison(Comparison::In),
3852 lhs: lhs.get_type(),
3853 rhs: rhs.get_type(),
3854 op_span: op,
3855 lhs_span: lhs.span(),
3856 rhs_span: rhs.span(),
3857 help: None,
3858 }
3859 } else {
3860 ShellError::OperatorUnsupportedType {
3861 op: Operator::Comparison(Comparison::In),
3862 unsupported: rhs.get_type(),
3863 op_span: op,
3864 unsupported_span: rhs.span(),
3865 help: None,
3866 }
3867 },
3868 ),
3869 }
3870 }
3871
3872 pub fn not_in(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3873 match (self, rhs) {
3874 (lhs, Value::Range { val: rhs, .. }) => Ok(Value::bool(!rhs.contains(lhs), span)),
3875 (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
3876 Ok(Value::bool(!rhs.contains(lhs), span))
3877 }
3878 (lhs, Value::List { vals: rhs, .. }) => Ok(Value::bool(!rhs.contains(lhs), span)),
3879 (Value::String { val: lhs, .. }, Value::Record { val: rhs, .. }) => {
3880 Ok(Value::bool(!rhs.contains(lhs), span))
3881 }
3882 (Value::String { .. } | Value::Int { .. }, Value::CellPath { val: rhs, .. }) => {
3883 let val = rhs.members.iter().any(|member| match (self, member) {
3884 (Value::Int { val: lhs, .. }, PathMember::Int { val: rhs, .. }) => {
3885 *lhs != *rhs as i64
3886 }
3887 (Value::String { val: lhs, .. }, PathMember::String { val: rhs, .. }) => {
3888 lhs != rhs
3889 }
3890 (Value::String { .. }, PathMember::Int { .. })
3891 | (Value::Int { .. }, PathMember::String { .. }) => true,
3892 _ => unreachable!(
3893 "outer match arm ensures `self` is either a `String` or `Int` variant"
3894 ),
3895 });
3896
3897 Ok(Value::bool(val, span))
3898 }
3899 (Value::CellPath { val: lhs, .. }, Value::CellPath { val: rhs, .. }) => {
3900 Ok(Value::bool(
3901 rhs.members
3902 .windows(lhs.members.len())
3903 .all(|member_window| member_window != rhs.members),
3904 span,
3905 ))
3906 }
3907 (Value::Custom { val: lhs, .. }, rhs) => lhs.operation(
3908 self.span(),
3909 Operator::Comparison(Comparison::NotIn),
3910 op,
3911 rhs,
3912 ),
3913 (lhs, rhs) => Err(
3914 if let Value::List { .. }
3915 | Value::Range { .. }
3916 | Value::String { .. }
3917 | Value::Record { .. }
3918 | Value::Custom { .. } = rhs
3919 {
3920 ShellError::OperatorIncompatibleTypes {
3921 op: Operator::Comparison(Comparison::NotIn),
3922 lhs: lhs.get_type(),
3923 rhs: rhs.get_type(),
3924 op_span: op,
3925 lhs_span: lhs.span(),
3926 rhs_span: rhs.span(),
3927 help: None,
3928 }
3929 } else {
3930 ShellError::OperatorUnsupportedType {
3931 op: Operator::Comparison(Comparison::NotIn),
3932 unsupported: rhs.get_type(),
3933 op_span: op,
3934 unsupported_span: rhs.span(),
3935 help: None,
3936 }
3937 },
3938 ),
3939 }
3940 }
3941
3942 pub fn has(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3943 rhs.r#in(op, self, span)
3944 }
3945
3946 pub fn not_has(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
3947 rhs.r#not_in(op, self, span)
3948 }
3949
3950 pub fn regex_match(
3951 &self,
3952 engine_state: &EngineState,
3953 op: Span,
3954 rhs: &Value,
3955 invert: bool,
3956 span: Span,
3957 ) -> Result<Value, ShellError> {
3958 let rhs_span = rhs.span();
3959 match (self, rhs) {
3960 (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
3961 let is_match = match engine_state.regex_cache.try_lock() {
3962 Ok(mut cache) => {
3963 if let Some(regex) = cache.get(rhs) {
3964 regex.is_match(lhs)
3965 } else {
3966 let regex =
3967 Regex::new(rhs).map_err(|e| ShellError::UnsupportedInput {
3968 msg: format!("{e}"),
3969 input: "value originated from here".into(),
3970 msg_span: span,
3971 input_span: rhs_span,
3972 })?;
3973 let ret = regex.is_match(lhs);
3974 cache.put(rhs.clone(), regex);
3975 ret
3976 }
3977 }
3978 Err(_) => {
3979 let regex = Regex::new(rhs).map_err(|e| ShellError::UnsupportedInput {
3980 msg: format!("{e}"),
3981 input: "value originated from here".into(),
3982 msg_span: span,
3983 input_span: rhs_span,
3984 })?;
3985 regex.is_match(lhs)
3986 }
3987 };
3988
3989 Ok(Value::bool(
3990 if invert {
3991 !is_match.unwrap_or(false)
3992 } else {
3993 is_match.unwrap_or(true)
3994 },
3995 span,
3996 ))
3997 }
3998 (Value::Custom { val: lhs, .. }, rhs) => lhs.operation(
3999 span,
4000 if invert {
4001 Operator::Comparison(Comparison::NotRegexMatch)
4002 } else {
4003 Operator::Comparison(Comparison::RegexMatch)
4004 },
4005 op,
4006 rhs,
4007 ),
4008 _ => Err(operator_type_error(
4009 if invert {
4010 Operator::Comparison(Comparison::NotRegexMatch)
4011 } else {
4012 Operator::Comparison(Comparison::RegexMatch)
4013 },
4014 op,
4015 self,
4016 rhs,
4017 |val| matches!(val, Value::String { .. }),
4018 )),
4019 }
4020 }
4021
4022 pub fn starts_with(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
4023 match (self, rhs) {
4024 (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
4025 Ok(Value::bool(lhs.starts_with(rhs), span))
4026 }
4027 (Value::Custom { val: lhs, .. }, rhs) => lhs.operation(
4028 self.span(),
4029 Operator::Comparison(Comparison::StartsWith),
4030 op,
4031 rhs,
4032 ),
4033 _ => Err(operator_type_error(
4034 Operator::Comparison(Comparison::StartsWith),
4035 op,
4036 self,
4037 rhs,
4038 |val| matches!(val, Value::String { .. }),
4039 )),
4040 }
4041 }
4042
4043 pub fn not_starts_with(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
4044 match (self, rhs) {
4045 (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
4046 Ok(Value::bool(!lhs.starts_with(rhs), span))
4047 }
4048 (Value::Custom { val: lhs, .. }, rhs) => lhs.operation(
4049 self.span(),
4050 Operator::Comparison(Comparison::NotStartsWith),
4051 op,
4052 rhs,
4053 ),
4054 _ => Err(operator_type_error(
4055 Operator::Comparison(Comparison::NotStartsWith),
4056 op,
4057 self,
4058 rhs,
4059 |val| matches!(val, Value::String { .. }),
4060 )),
4061 }
4062 }
4063
4064 pub fn ends_with(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
4065 match (self, rhs) {
4066 (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
4067 Ok(Value::bool(lhs.ends_with(rhs), span))
4068 }
4069 (Value::Custom { val: lhs, .. }, rhs) => lhs.operation(
4070 self.span(),
4071 Operator::Comparison(Comparison::EndsWith),
4072 op,
4073 rhs,
4074 ),
4075 _ => Err(operator_type_error(
4076 Operator::Comparison(Comparison::EndsWith),
4077 op,
4078 self,
4079 rhs,
4080 |val| matches!(val, Value::String { .. }),
4081 )),
4082 }
4083 }
4084
4085 pub fn not_ends_with(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
4086 match (self, rhs) {
4087 (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
4088 Ok(Value::bool(!lhs.ends_with(rhs), span))
4089 }
4090 (Value::Custom { val: lhs, .. }, rhs) => lhs.operation(
4091 self.span(),
4092 Operator::Comparison(Comparison::NotEndsWith),
4093 op,
4094 rhs,
4095 ),
4096 _ => Err(operator_type_error(
4097 Operator::Comparison(Comparison::NotEndsWith),
4098 op,
4099 self,
4100 rhs,
4101 |val| matches!(val, Value::String { .. }),
4102 )),
4103 }
4104 }
4105
4106 pub fn bit_or(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
4107 match (self, rhs) {
4108 (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
4109 Ok(Value::int(*lhs | rhs, span))
4110 }
4111 (Value::Custom { val: lhs, .. }, rhs) => {
4112 lhs.operation(span, Operator::Bits(Bits::BitOr), op, rhs)
4113 }
4114 _ => Err(operator_type_error(
4115 Operator::Bits(Bits::BitOr),
4116 op,
4117 self,
4118 rhs,
4119 |val| matches!(val, Value::Int { .. }),
4120 )),
4121 }
4122 }
4123
4124 pub fn bit_xor(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
4125 match (self, rhs) {
4126 (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
4127 Ok(Value::int(*lhs ^ rhs, span))
4128 }
4129 (Value::Custom { val: lhs, .. }, rhs) => {
4130 lhs.operation(span, Operator::Bits(Bits::BitXor), op, rhs)
4131 }
4132 _ => Err(operator_type_error(
4133 Operator::Bits(Bits::BitXor),
4134 op,
4135 self,
4136 rhs,
4137 |val| matches!(val, Value::Int { .. }),
4138 )),
4139 }
4140 }
4141
4142 pub fn bit_and(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
4143 match (self, rhs) {
4144 (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
4145 Ok(Value::int(*lhs & rhs, span))
4146 }
4147 (Value::Custom { val: lhs, .. }, rhs) => {
4148 lhs.operation(span, Operator::Bits(Bits::BitAnd), op, rhs)
4149 }
4150 _ => Err(operator_type_error(
4151 Operator::Bits(Bits::BitAnd),
4152 op,
4153 self,
4154 rhs,
4155 |val| matches!(val, Value::Int { .. }),
4156 )),
4157 }
4158 }
4159
4160 pub fn bit_shl(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
4161 match (self, rhs) {
4162 (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
4163 if let Some(val) = (*rhs).try_into().ok().and_then(|rhs| lhs.checked_shl(rhs)) {
4166 Ok(Value::int(val, span))
4167 } else {
4168 Err(ShellError::OperatorOverflow {
4169 msg: "right operand to bit-shl exceeds available bits in underlying data"
4170 .into(),
4171 span,
4172 help: Some(format!("Limit operand to 0 <= rhs < {}", i64::BITS)),
4173 })
4174 }
4175 }
4176 (Value::Custom { val: lhs, .. }, rhs) => {
4177 lhs.operation(span, Operator::Bits(Bits::ShiftLeft), op, rhs)
4178 }
4179 _ => Err(operator_type_error(
4180 Operator::Bits(Bits::ShiftLeft),
4181 op,
4182 self,
4183 rhs,
4184 |val| matches!(val, Value::Int { .. }),
4185 )),
4186 }
4187 }
4188
4189 pub fn bit_shr(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
4190 match (self, rhs) {
4191 (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
4192 if let Some(val) = (*rhs).try_into().ok().and_then(|rhs| lhs.checked_shr(rhs)) {
4195 Ok(Value::int(val, span))
4196 } else {
4197 Err(ShellError::OperatorOverflow {
4198 msg: "right operand to bit-shr exceeds available bits in underlying data"
4199 .into(),
4200 span,
4201 help: Some(format!("Limit operand to 0 <= rhs < {}", i64::BITS)),
4202 })
4203 }
4204 }
4205 (Value::Custom { val: lhs, .. }, rhs) => {
4206 lhs.operation(span, Operator::Bits(Bits::ShiftRight), op, rhs)
4207 }
4208 _ => Err(operator_type_error(
4209 Operator::Bits(Bits::ShiftRight),
4210 op,
4211 self,
4212 rhs,
4213 |val| matches!(val, Value::Int { .. }),
4214 )),
4215 }
4216 }
4217
4218 pub fn or(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
4219 match (self, rhs) {
4220 (Value::Bool { val: lhs, .. }, Value::Bool { val: rhs, .. }) => {
4221 Ok(Value::bool(*lhs || *rhs, span))
4222 }
4223 (Value::Custom { val: lhs, .. }, rhs) => {
4224 lhs.operation(span, Operator::Boolean(Boolean::Or), op, rhs)
4225 }
4226 _ => Err(operator_type_error(
4227 Operator::Boolean(Boolean::Or),
4228 op,
4229 self,
4230 rhs,
4231 |val| matches!(val, Value::Bool { .. }),
4232 )),
4233 }
4234 }
4235
4236 pub fn xor(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
4237 match (self, rhs) {
4238 (Value::Bool { val: lhs, .. }, Value::Bool { val: rhs, .. }) => {
4239 Ok(Value::bool((*lhs && !*rhs) || (!*lhs && *rhs), span))
4240 }
4241 (Value::Custom { val: lhs, .. }, rhs) => {
4242 lhs.operation(span, Operator::Boolean(Boolean::Xor), op, rhs)
4243 }
4244 _ => Err(operator_type_error(
4245 Operator::Boolean(Boolean::Xor),
4246 op,
4247 self,
4248 rhs,
4249 |val| matches!(val, Value::Bool { .. }),
4250 )),
4251 }
4252 }
4253
4254 pub fn and(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
4255 match (self, rhs) {
4256 (Value::Bool { val: lhs, .. }, Value::Bool { val: rhs, .. }) => {
4257 Ok(Value::bool(*lhs && *rhs, span))
4258 }
4259 (Value::Custom { val: lhs, .. }, rhs) => {
4260 lhs.operation(span, Operator::Boolean(Boolean::And), op, rhs)
4261 }
4262 _ => Err(operator_type_error(
4263 Operator::Boolean(Boolean::And),
4264 op,
4265 self,
4266 rhs,
4267 |val| matches!(val, Value::Bool { .. }),
4268 )),
4269 }
4270 }
4271}
4272
4273fn type_compatible(a: Type, b: Type) -> bool {
4276 if a == b {
4277 return true;
4278 }
4279
4280 matches!((a, b), (Type::Int, Type::Float) | (Type::Float, Type::Int))
4281}
4282
4283fn operator_type_error(
4284 op: Operator,
4285 op_span: Span,
4286 lhs: &Value,
4287 rhs: &Value,
4288 is_supported: fn(&Value) -> bool,
4289) -> ShellError {
4290 let is_supported = |val| is_supported(val) || matches!(val, Value::Custom { .. });
4291 match (is_supported(lhs), is_supported(rhs)) {
4292 (true, true) => ShellError::OperatorIncompatibleTypes {
4293 op,
4294 lhs: lhs.get_type(),
4295 rhs: rhs.get_type(),
4296 op_span,
4297 lhs_span: lhs.span(),
4298 rhs_span: rhs.span(),
4299 help: None,
4300 },
4301 (true, false) => ShellError::OperatorUnsupportedType {
4302 op,
4303 unsupported: rhs.get_type(),
4304 op_span,
4305 unsupported_span: rhs.span(),
4306 help: None,
4307 },
4308 (false, _) => ShellError::OperatorUnsupportedType {
4309 op,
4310 unsupported: lhs.get_type(),
4311 op_span,
4312 unsupported_span: lhs.span(),
4313 help: None,
4314 },
4315 }
4316}
4317
4318pub fn human_time_from_now(val: &DateTime<FixedOffset>) -> HumanTime {
4319 let now = Local::now().with_timezone(val.offset());
4320 let delta = *val - now;
4321 match delta.num_nanoseconds() {
4322 Some(num_nanoseconds) => {
4323 let delta_seconds = num_nanoseconds as f64 / 1_000_000_000.0;
4324 let delta_seconds_rounded = delta_seconds.round() as i64;
4325 HumanTime::from(Duration::seconds(delta_seconds_rounded))
4326 }
4327 None => {
4328 let delta_years = val.year() - now.year();
4331 HumanTime::from(Duration::days(delta_years as i64 * 365))
4332 }
4333 }
4334}
4335
4336#[cfg(test)]
4337mod tests {
4338 use super::{Record, Value};
4339 use crate::record;
4340 use indoc::indoc;
4341
4342 mod debug {
4343 use super::*;
4344 use crate::{
4345 BlockId, CustomValue, IntRange, Range, ShellError, Span, VarId,
4346 ast::{CellPath, PathMember},
4347 casing::Casing,
4348 engine::Closure,
4349 };
4350 use chrono::DateTime;
4351 use pretty_assertions::assert_eq;
4352 use serde::{Deserialize, Serialize};
4353 use std::ops::Bound;
4354
4355 #[derive(Debug, Clone, Serialize, Deserialize)]
4356 struct TinyCustomValue;
4357
4358 #[typetag::serde]
4359 impl CustomValue for TinyCustomValue {
4360 fn clone_value(&self, span: Span) -> Value {
4361 Value::custom(Box::new(self.clone()), span)
4362 }
4363
4364 fn type_name(&self) -> String {
4365 "TinyCustomValue".into()
4366 }
4367
4368 fn to_base_value(&self, span: Span) -> Result<Value, ShellError> {
4369 Ok(Value::nothing(span))
4370 }
4371
4372 fn as_any(&self) -> &dyn std::any::Any {
4373 self
4374 }
4375
4376 fn as_mut_any(&mut self) -> &mut dyn std::any::Any {
4377 self
4378 }
4379 }
4380
4381 struct DebugFormats {
4382 value: Value,
4383 expanded: &'static str,
4384 expanded_alternate: &'static str,
4385 compact: &'static str,
4386 compact_alternate: &'static str,
4387 }
4388
4389 impl DebugFormats {
4390 #[track_caller]
4391 fn assert(&self) {
4392 let value = &self.value;
4393 assert_eq!(format!("{value:-?}"), self.expanded);
4394 assert_eq!(format!("{value:-#?}"), self.expanded_alternate);
4395 assert_eq!(format!("{value:?}"), self.compact);
4396 assert_eq!(format!("{value:#?}"), self.compact_alternate);
4397 }
4398 }
4399
4400 #[test]
4401 fn bool() {
4402 let value = Value::test_bool(true);
4403 DebugFormats {
4404 value,
4405 expanded: "Bool { val: true, internal_span: Span(TEST) }",
4406 expanded_alternate: indoc! {"
4407 Bool {
4408 val: true,
4409 internal_span: Span(TEST),
4410 }"
4411 },
4412 compact: "Bool(true)",
4413 compact_alternate: "Bool(true)",
4414 }
4415 .assert();
4416 }
4417
4418 #[test]
4419 fn int() {
4420 let value = Value::test_int(42);
4421 DebugFormats {
4422 value,
4423 expanded: "Int { val: 42, internal_span: Span(TEST) }",
4424 expanded_alternate: indoc! {"
4425 Int {
4426 val: 42,
4427 internal_span: Span(TEST),
4428 }"
4429 },
4430 compact: "Int(42)",
4431 compact_alternate: "Int(42)",
4432 }
4433 .assert();
4434 }
4435
4436 #[test]
4437 fn float() {
4438 let value = Value::test_float(4.2);
4439 DebugFormats {
4440 value,
4441 expanded: "Float { val: 4.2, internal_span: Span(TEST) }",
4442 expanded_alternate: indoc! {"
4443 Float {
4444 val: 4.2,
4445 internal_span: Span(TEST),
4446 }"
4447 },
4448 compact: "Float(4.2)",
4449 compact_alternate: "Float(4.2)",
4450 }
4451 .assert();
4452 }
4453
4454 #[test]
4455 fn filesize() {
4456 let value = Value::test_filesize(42);
4457 DebugFormats {
4458 value,
4459 expanded: "Filesize { val: Filesize(42), internal_span: Span(TEST) }",
4460 expanded_alternate: indoc! {"
4461 Filesize {
4462 val: Filesize(
4463 42,
4464 ),
4465 internal_span: Span(TEST),
4466 }"
4467 },
4468 compact: "Filesize(42 B)",
4469 compact_alternate: "Filesize(42 B)",
4470 }
4471 .assert();
4472 }
4473
4474 #[test]
4475 fn duration() {
4476 let value = Value::test_duration(42);
4477 DebugFormats {
4478 value,
4479 expanded: "Duration { val: 42, internal_span: Span(TEST) }",
4480 expanded_alternate: indoc! {"
4481 Duration {
4482 val: 42,
4483 internal_span: Span(TEST),
4484 }"
4485 },
4486 compact: "Duration(42ns)",
4487 compact_alternate: "Duration(42ns)",
4488 }
4489 .assert();
4490 }
4491
4492 #[test]
4493 fn date() {
4494 let value = Value::test_date(DateTime::UNIX_EPOCH.into());
4495 DebugFormats {
4496 value,
4497 expanded: "Date { val: 1970-01-01T00:00:00+00:00, internal_span: Span(TEST) }",
4498 expanded_alternate: indoc! {"
4499 Date {
4500 val: 1970-01-01T00:00:00+00:00,
4501 internal_span: Span(TEST),
4502 }"
4503 },
4504 compact: "Date(1970-01-01T00:00:00+00:00)",
4505 compact_alternate: "Date(1970-01-01T00:00:00+00:00)",
4506 }
4507 .assert();
4508 }
4509
4510 #[test]
4511 fn range() {
4512 let value = Value::test_range(Range::IntRange(IntRange {
4513 start: 1,
4514 step: 2,
4515 end: Bound::Excluded(5),
4516 }));
4517 DebugFormats {
4518 value,
4519 expanded: "Range { val: IntRange(IntRange { start: 1, step: 2, end: Excluded(5) }), signals: None, internal_span: Span(TEST) }",
4520 expanded_alternate: indoc! {"
4521 Range {
4522 val: IntRange(
4523 IntRange {
4524 start: 1,
4525 step: 2,
4526 end: Excluded(
4527 5,
4528 ),
4529 },
4530 ),
4531 signals: None,
4532 internal_span: Span(TEST),
4533 }"
4534 },
4535 compact: "Range(1..3..<5)",
4536 compact_alternate: "Range(1..3..<5)",
4537 }
4538 .assert();
4539 }
4540
4541 #[test]
4542 fn string() {
4543 let value = Value::test_string("Ellie");
4544 DebugFormats {
4545 value,
4546 expanded: r#"String { val: "Ellie", internal_span: Span(TEST) }"#,
4547 expanded_alternate: indoc! {r#"
4548 String {
4549 val: "Ellie",
4550 internal_span: Span(TEST),
4551 }"#
4552 },
4553 compact: r#"String("Ellie")"#,
4554 compact_alternate: r#"String("Ellie")"#,
4555 }
4556 .assert();
4557 }
4558
4559 #[test]
4560 fn glob() {
4561 let value = Value::test_glob("*.nu");
4562 DebugFormats {
4563 value,
4564 expanded: r#"Glob { val: "*.nu", no_expand: false, internal_span: Span(TEST) }"#,
4565 expanded_alternate: indoc! {r#"
4566 Glob {
4567 val: "*.nu",
4568 no_expand: false,
4569 internal_span: Span(TEST),
4570 }"#
4571 },
4572 compact: r#"Glob("*.nu")"#,
4573 compact_alternate: r#"Glob("*.nu")"#,
4574 }
4575 .assert();
4576 }
4577
4578 #[test]
4579 fn record() {
4580 let value = Value::test_record(record!("name" => Value::test_string("Ellie")));
4581 DebugFormats {
4582 value,
4583 expanded: r#"Record { val: {"name": String { val: "Ellie", internal_span: Span(TEST) }}, internal_span: Span(TEST) }"#,
4584 expanded_alternate: indoc! {r#"
4585 Record {
4586 val: {
4587 "name": String {
4588 val: "Ellie",
4589 internal_span: Span(TEST),
4590 },
4591 },
4592 internal_span: Span(TEST),
4593 }"#
4594 },
4595 compact: r#"Record({"name": String("Ellie")})"#,
4596 compact_alternate: indoc! {r#"
4597 Record({
4598 "name": String("Ellie"),
4599 })"#
4600 },
4601 }
4602 .assert();
4603 }
4604
4605 #[test]
4606 fn list() {
4607 let value = Value::test_list(vec![Value::test_int(42), Value::test_string("Ellie")]);
4608 DebugFormats {
4609 value,
4610 expanded: r#"List { vals: [Int { val: 42, internal_span: Span(TEST) }, String { val: "Ellie", internal_span: Span(TEST) }], signals: None, internal_span: Span(TEST) }"#,
4611 expanded_alternate: indoc! {r#"
4612 List {
4613 vals: [
4614 Int {
4615 val: 42,
4616 internal_span: Span(TEST),
4617 },
4618 String {
4619 val: "Ellie",
4620 internal_span: Span(TEST),
4621 },
4622 ],
4623 signals: None,
4624 internal_span: Span(TEST),
4625 }"#
4626 },
4627 compact: r#"List([Int(42), String("Ellie")])"#,
4628 compact_alternate: indoc! {r#"
4629 List([
4630 Int(42),
4631 String("Ellie"),
4632 ])"#
4633 },
4634 }
4635 .assert();
4636 }
4637
4638 #[test]
4639 fn closure() {
4640 let value = Value::test_closure(Closure {
4641 block_id: BlockId::new(42),
4642 captures: vec![(VarId::new(7), Value::test_int(1))],
4643 });
4644 DebugFormats {
4645 value,
4646 expanded: "Closure { val: Closure { block_id: BlockId(42), captures: {VarId(7): Int { val: 1, internal_span: Span(TEST) }} }, internal_span: Span(TEST) }",
4647 expanded_alternate: indoc! {"
4648 Closure {
4649 val: Closure {
4650 block_id: BlockId(42),
4651 captures: {
4652 VarId(7): Int {
4653 val: 1,
4654 internal_span: Span(TEST),
4655 },
4656 },
4657 },
4658 internal_span: Span(TEST),
4659 }"
4660 },
4661 compact: "Closure(BlockId(42): {VarId(7): Int(1)})",
4662 compact_alternate: indoc! {"
4663 Closure(BlockId(42): {
4664 VarId(7): Int(1),
4665 })"
4666 },
4667 }
4668 .assert();
4669 }
4670
4671 #[test]
4672 fn error() {
4673 let value = Value::error(
4674 ShellError::NushellFailed { msg: "oops".into() },
4675 Span::test_data(),
4676 );
4677 DebugFormats {
4678 value,
4679 expanded: r#"Error { error: NushellFailed { msg: "oops" }, internal_span: Span(TEST) }"#,
4680 expanded_alternate: indoc! {r#"
4681 Error {
4682 error: NushellFailed {
4683 msg: "oops",
4684 },
4685 internal_span: Span(TEST),
4686 }"#
4687 },
4688 compact: r#"Error(NushellFailed { msg: "oops" })"#,
4689 compact_alternate: indoc! {r#"
4690 Error(NushellFailed {
4691 msg: "oops",
4692 })"#
4693 },
4694 }
4695 .assert();
4696 }
4697
4698 #[test]
4699 fn binary() {
4700 let mut bytes = b"Ellie".to_vec();
4701 bytes.extend([0xFF]);
4702 let value = Value::test_binary(bytes);
4703 DebugFormats {
4704 value,
4705 expanded: "Binary { val: [69, 108, 108, 105, 101, 255], internal_span: Span(TEST) }",
4706 expanded_alternate: indoc! {"
4707 Binary {
4708 val: [
4709 69,
4710 108,
4711 108,
4712 105,
4713 101,
4714 255,
4715 ],
4716 internal_span: Span(TEST),
4717 }"
4718 },
4719 compact: r#"Binary("Ellie\xff")"#,
4720 compact_alternate: r#"Binary("Ellie\xff")"#,
4721 }
4722 .assert();
4723 }
4724
4725 #[test]
4726 fn cell_path() {
4727 let value = Value::test_cell_path(CellPath {
4728 members: vec![
4729 PathMember::test_string("name".into(), false, Casing::Sensitive),
4730 PathMember::test_int(1, true),
4731 ],
4732 });
4733 DebugFormats {
4734 value,
4735 expanded: r#"CellPath { val: CellPath { members: [String { val: "name", span: Span(TEST), optional: false, casing: Sensitive }, Int { val: 1, span: Span(TEST), optional: true }] }, internal_span: Span(TEST) }"#,
4736 expanded_alternate: indoc! {r#"
4737 CellPath {
4738 val: CellPath {
4739 members: [
4740 String {
4741 val: "name",
4742 span: Span(TEST),
4743 optional: false,
4744 casing: Sensitive,
4745 },
4746 Int {
4747 val: 1,
4748 span: Span(TEST),
4749 optional: true,
4750 },
4751 ],
4752 },
4753 internal_span: Span(TEST),
4754 }"#
4755 },
4756 compact: "CellPath($.name.1?)",
4757 compact_alternate: "CellPath($.name.1?)",
4758 }
4759 .assert();
4760 }
4761
4762 #[test]
4763 fn nothing() {
4764 let value = Value::test_nothing();
4765 DebugFormats {
4766 value,
4767 expanded: "Nothing { internal_span: Span(TEST) }",
4768 expanded_alternate: indoc! {"
4769 Nothing {
4770 internal_span: Span(TEST),
4771 }"
4772 },
4773 compact: "Nothing",
4774 compact_alternate: "Nothing",
4775 }
4776 .assert();
4777 }
4778
4779 #[test]
4780 fn custom() {
4781 let value = Value::test_custom_value(Box::new(TinyCustomValue));
4782 DebugFormats {
4783 value,
4784 expanded: "Custom { val: TinyCustomValue, internal_span: Span(TEST) }",
4785 expanded_alternate: indoc! {"
4786 Custom {
4787 val: TinyCustomValue,
4788 internal_span: Span(TEST),
4789 }"
4790 },
4791 compact: "Custom(TinyCustomValue)",
4792 compact_alternate: "Custom(TinyCustomValue)",
4793 }
4794 .assert();
4795 }
4796 }
4797
4798 mod at_cell_path {
4799 use crate::casing::Casing;
4800
4801 use crate::{IntoValue, ShellError, Span};
4802
4803 use super::super::PathMember;
4804 use super::*;
4805
4806 #[test]
4807 fn test_record_with_data_at_cell_path() {
4808 let value_to_insert = Value::test_string("value");
4809 let span = Span::test_data();
4810 assert_eq!(
4811 Value::with_data_at_cell_path(
4812 &[
4813 PathMember::test_string("a".to_string(), false, Casing::Sensitive),
4814 PathMember::test_string("b".to_string(), false, Casing::Sensitive),
4815 PathMember::test_string("c".to_string(), false, Casing::Sensitive),
4816 PathMember::test_string("d".to_string(), false, Casing::Sensitive),
4817 ],
4818 value_to_insert,
4819 ),
4820 Ok(record!(
4822 "a" => record!(
4823 "b" => record!(
4824 "c" => record!(
4825 "d" => Value::test_string("value")
4826 ).into_value(span)
4827 ).into_value(span)
4828 ).into_value(span)
4829 )
4830 .into_value(span))
4831 );
4832 }
4833
4834 #[test]
4835 fn test_lists_with_data_at_cell_path() {
4836 let value_to_insert = Value::test_string("value");
4837 assert_eq!(
4838 Value::with_data_at_cell_path(
4839 &[
4840 PathMember::test_int(0, false),
4841 PathMember::test_int(0, false),
4842 PathMember::test_int(0, false),
4843 PathMember::test_int(0, false),
4844 ],
4845 value_to_insert.clone(),
4846 ),
4847 Ok(Value::test_list(vec![Value::test_list(vec![
4849 Value::test_list(vec![Value::test_list(vec![value_to_insert])])
4850 ])]))
4851 );
4852 }
4853 #[test]
4854 fn test_mixed_with_data_at_cell_path() {
4855 let value_to_insert = Value::test_string("value");
4856 let span = Span::test_data();
4857 assert_eq!(
4858 Value::with_data_at_cell_path(
4859 &[
4860 PathMember::test_string("a".to_string(), false, Casing::Sensitive),
4861 PathMember::test_int(0, false),
4862 PathMember::test_string("b".to_string(), false, Casing::Sensitive),
4863 PathMember::test_int(0, false),
4864 PathMember::test_string("c".to_string(), false, Casing::Sensitive),
4865 PathMember::test_int(0, false),
4866 PathMember::test_string("d".to_string(), false, Casing::Sensitive),
4867 PathMember::test_int(0, false),
4868 ],
4869 value_to_insert.clone(),
4870 ),
4871 Ok(record!(
4873 "a" => Value::test_list(vec![record!(
4874 "b" => Value::test_list(vec![record!(
4875 "c" => Value::test_list(vec![record!(
4876 "d" => Value::test_list(vec![value_to_insert])
4877 ).into_value(span)])
4878 ).into_value(span)])
4879 ).into_value(span)])
4880 )
4881 .into_value(span))
4882 );
4883 }
4884
4885 #[test]
4886 fn test_nested_upsert_data_at_cell_path() {
4887 let span = Span::test_data();
4888 let mut base_value = record!(
4889 "a" => Value::test_list(vec![])
4890 )
4891 .into_value(span);
4892
4893 let value_to_insert = Value::test_string("value");
4894 let res = base_value.upsert_data_at_cell_path(
4895 &[
4896 PathMember::test_string("a".to_string(), false, Casing::Sensitive),
4897 PathMember::test_int(0, false),
4898 PathMember::test_string("b".to_string(), false, Casing::Sensitive),
4899 PathMember::test_int(0, false),
4900 ],
4901 value_to_insert.clone(),
4902 );
4903 assert_eq!(res, Ok(()));
4904 assert_eq!(
4905 base_value,
4906 record!(
4908 "a" => Value::test_list(vec![
4909 record!(
4910 "b" => Value::test_list(vec![value_to_insert])
4911 )
4912 .into_value(span)
4913 ])
4914 )
4915 .into_value(span)
4916 );
4917 }
4918
4919 #[test]
4920 fn test_nested_insert_data_at_cell_path() {
4921 let span = Span::test_data();
4922 let mut base_value = record!(
4923 "a" => Value::test_list(vec![])
4924 )
4925 .into_value(span);
4926
4927 let value_to_insert = Value::test_string("value");
4928 let res = base_value.insert_data_at_cell_path(
4929 &[
4930 PathMember::test_string("a".to_string(), false, Casing::Sensitive),
4931 PathMember::test_int(0, false),
4932 PathMember::test_string("b".to_string(), false, Casing::Sensitive),
4933 PathMember::test_int(0, false),
4934 ],
4935 value_to_insert.clone(),
4936 span,
4937 );
4938 assert_eq!(res, Ok(()));
4939 assert_eq!(
4940 base_value,
4941 record!(
4943 "a" => Value::test_list(vec![
4944 record!(
4945 "b" => Value::test_list(vec![value_to_insert])
4946 )
4947 .into_value(span)
4948 ])
4949 )
4950 .into_value(span)
4951 );
4952 }
4953
4954 #[test]
4955 fn update_existing_record_field() {
4956 let span = Span::test_data();
4957 let mut val = record!("a" => Value::test_int(1)).into_value(span);
4958 let res = val.update_data_at_cell_path(
4959 &[PathMember::test_string(
4960 "a".into(),
4961 false,
4962 Casing::Sensitive,
4963 )],
4964 Value::test_int(2),
4965 );
4966 assert_eq!(res, Ok(()));
4967 assert_eq!(val, record!("a" => Value::test_int(2)).into_value(span));
4968 }
4969
4970 #[test]
4971 fn update_existing_list_element() {
4972 let mut val = Value::test_list(vec![Value::test_int(10), Value::test_int(20)]);
4973 let res = val
4974 .update_data_at_cell_path(&[PathMember::test_int(1, false)], Value::test_int(99));
4975 assert_eq!(res, Ok(()));
4976 assert_eq!(
4977 val,
4978 Value::test_list(vec![Value::test_int(10), Value::test_int(99)])
4979 );
4980 }
4981
4982 #[test]
4983 fn update_missing_record_field_errors() {
4984 let span = Span::test_data();
4985 let mut val = record!("a" => Value::test_int(1)).into_value(span);
4986 let res = val.update_data_at_cell_path(
4987 &[PathMember::test_string(
4988 "b".into(),
4989 false,
4990 Casing::Sensitive,
4991 )],
4992 Value::test_int(2),
4993 );
4994 assert!(matches!(res, Err(ShellError::CantFindColumn { .. })));
4995 }
4996
4997 #[test]
4998 fn update_out_of_bounds_list_errors() {
4999 let mut val = Value::test_list(vec![Value::test_int(1)]);
5000 let res =
5001 val.update_data_at_cell_path(&[PathMember::test_int(5, false)], Value::test_int(2));
5002 assert!(matches!(res, Err(ShellError::AccessBeyondEnd { .. })));
5003 }
5004
5005 #[test]
5006 fn update_empty_list_errors() {
5007 let mut val = Value::test_list(vec![]);
5008 let res =
5009 val.update_data_at_cell_path(&[PathMember::test_int(0, false)], Value::test_int(2));
5010 assert!(matches!(res, Err(ShellError::AccessEmptyContent { .. })));
5011 }
5012
5013 #[test]
5014 fn update_optional_missing_field_ok() {
5015 let span = Span::test_data();
5016 let mut val = record!("a" => Value::test_int(1)).into_value(span);
5017 let res = val.update_data_at_cell_path(
5018 &[PathMember::test_string("z".into(), true, Casing::Sensitive)],
5019 Value::test_int(2),
5020 );
5021 assert_eq!(res, Ok(()));
5022 assert_eq!(val, record!("a" => Value::test_int(1)).into_value(span));
5023 }
5024
5025 #[test]
5026 fn update_optional_out_of_bounds_ok() {
5027 let mut val = Value::test_list(vec![Value::test_int(1)]);
5028 let res =
5029 val.update_data_at_cell_path(&[PathMember::test_int(5, true)], Value::test_int(2));
5030 assert_eq!(res, Ok(()));
5031 assert_eq!(val, Value::test_list(vec![Value::test_int(1)]));
5032 }
5033
5034 #[test]
5035 fn update_nested_record_field() {
5036 let span = Span::test_data();
5037 let mut val = record!(
5038 "a" => record!("b" => Value::test_int(1)).into_value(span)
5039 )
5040 .into_value(span);
5041 let res = val.update_data_at_cell_path(
5042 &[
5043 PathMember::test_string("a".into(), false, Casing::Sensitive),
5044 PathMember::test_string("b".into(), false, Casing::Sensitive),
5045 ],
5046 Value::test_int(99),
5047 );
5048 assert_eq!(res, Ok(()));
5049 assert_eq!(
5050 val,
5051 record!(
5052 "a" => record!("b" => Value::test_int(99)).into_value(span)
5053 )
5054 .into_value(span)
5055 );
5056 }
5057
5058 #[test]
5059 fn update_in_table() {
5060 let span = Span::test_data();
5061 let mut val = Value::test_list(vec![
5062 record!("x" => Value::test_int(1)).into_value(span),
5063 record!("x" => Value::test_int(2)).into_value(span),
5064 ]);
5065 let res = val.update_data_at_cell_path(
5066 &[PathMember::test_string(
5067 "x".into(),
5068 false,
5069 Casing::Sensitive,
5070 )],
5071 Value::test_int(0),
5072 );
5073 assert_eq!(res, Ok(()));
5074 assert_eq!(
5075 val,
5076 Value::test_list(vec![
5077 record!("x" => Value::test_int(0)).into_value(span),
5078 record!("x" => Value::test_int(0)).into_value(span),
5079 ])
5080 );
5081 }
5082
5083 #[test]
5084 fn remove_record_column() {
5085 let span = Span::test_data();
5086 let mut val =
5087 record!("a" => Value::test_int(1), "b" => Value::test_int(2)).into_value(span);
5088 let res = val.remove_data_at_cell_path(&[PathMember::test_string(
5089 "a".into(),
5090 false,
5091 Casing::Sensitive,
5092 )]);
5093 assert_eq!(res, Ok(()));
5094 assert_eq!(val, record!("b" => Value::test_int(2)).into_value(span));
5095 }
5096
5097 #[test]
5098 fn remove_list_element() {
5099 let mut val = Value::test_list(vec![
5100 Value::test_int(10),
5101 Value::test_int(20),
5102 Value::test_int(30),
5103 ]);
5104 let res = val.remove_data_at_cell_path(&[PathMember::test_int(1, false)]);
5105 assert_eq!(res, Ok(()));
5106 assert_eq!(
5107 val,
5108 Value::test_list(vec![Value::test_int(10), Value::test_int(30)])
5109 );
5110 }
5111
5112 #[test]
5113 fn remove_nested_field() {
5114 let span = Span::test_data();
5115 let mut val = record!(
5116 "a" => record!("b" => Value::test_int(1), "c" => Value::test_int(2)).into_value(span)
5117 )
5118 .into_value(span);
5119 let res = val.remove_data_at_cell_path(&[
5120 PathMember::test_string("a".into(), false, Casing::Sensitive),
5121 PathMember::test_string("b".into(), false, Casing::Sensitive),
5122 ]);
5123 assert_eq!(res, Ok(()));
5124 assert_eq!(
5125 val,
5126 record!(
5127 "a" => record!("c" => Value::test_int(2)).into_value(span)
5128 )
5129 .into_value(span)
5130 );
5131 }
5132
5133 #[test]
5134 fn remove_column_from_table() {
5135 let span = Span::test_data();
5136 let mut val = Value::test_list(vec![
5137 record!("x" => Value::test_int(1), "y" => Value::test_int(2)).into_value(span),
5138 record!("x" => Value::test_int(3), "y" => Value::test_int(4)).into_value(span),
5139 ]);
5140 let res = val.remove_data_at_cell_path(&[PathMember::test_string(
5141 "x".into(),
5142 false,
5143 Casing::Sensitive,
5144 )]);
5145 assert_eq!(res, Ok(()));
5146 assert_eq!(
5147 val,
5148 Value::test_list(vec![
5149 record!("y" => Value::test_int(2)).into_value(span),
5150 record!("y" => Value::test_int(4)).into_value(span),
5151 ])
5152 );
5153 }
5154
5155 #[test]
5156 fn upsert_overwrite_existing_record_field() {
5157 let span = Span::test_data();
5158 let mut val = record!("a" => Value::test_int(1)).into_value(span);
5159 let res = val.upsert_data_at_cell_path(
5160 &[PathMember::test_string(
5161 "a".into(),
5162 false,
5163 Casing::Sensitive,
5164 )],
5165 Value::test_int(99),
5166 );
5167 assert_eq!(res, Ok(()));
5168 assert_eq!(val, record!("a" => Value::test_int(99)).into_value(span));
5169 }
5170
5171 #[test]
5172 fn upsert_overwrite_existing_list_element() {
5173 let mut val = Value::test_list(vec![Value::test_int(10), Value::test_int(20)]);
5174 let res = val
5175 .upsert_data_at_cell_path(&[PathMember::test_int(0, false)], Value::test_int(99));
5176 assert_eq!(res, Ok(()));
5177 assert_eq!(
5178 val,
5179 Value::test_list(vec![Value::test_int(99), Value::test_int(20)])
5180 );
5181 }
5182
5183 #[test]
5184 fn upsert_creates_new_record_field() {
5185 let span = Span::test_data();
5186 let mut val = record!("a" => Value::test_int(1)).into_value(span);
5187 let res = val.upsert_data_at_cell_path(
5188 &[PathMember::test_string(
5189 "b".into(),
5190 false,
5191 Casing::Sensitive,
5192 )],
5193 Value::test_int(2),
5194 );
5195 assert_eq!(res, Ok(()));
5196 assert_eq!(
5197 val,
5198 record!("a" => Value::test_int(1), "b" => Value::test_int(2)).into_value(span)
5199 );
5200 }
5201
5202 #[test]
5203 fn upsert_appends_to_list() {
5204 let mut val = Value::test_list(vec![Value::test_int(1)]);
5205 let res =
5206 val.upsert_data_at_cell_path(&[PathMember::test_int(1, false)], Value::test_int(2));
5207 assert_eq!(res, Ok(()));
5208 assert_eq!(
5209 val,
5210 Value::test_list(vec![Value::test_int(1), Value::test_int(2)])
5211 );
5212 }
5213
5214 #[test]
5215 fn upsert_in_table() {
5216 let span = Span::test_data();
5217 let mut val = Value::test_list(vec![
5218 record!("x" => Value::test_int(1)).into_value(span),
5219 record!("x" => Value::test_int(2)).into_value(span),
5220 ]);
5221 let res = val.upsert_data_at_cell_path(
5222 &[PathMember::test_string(
5223 "x".into(),
5224 false,
5225 Casing::Sensitive,
5226 )],
5227 Value::test_int(0),
5228 );
5229 assert_eq!(res, Ok(()));
5230 assert_eq!(
5231 val,
5232 Value::test_list(vec![
5233 record!("x" => Value::test_int(0)).into_value(span),
5234 record!("x" => Value::test_int(0)).into_value(span),
5235 ])
5236 );
5237 }
5238
5239 #[test]
5240 fn insert_new_record_field() {
5241 let span = Span::test_data();
5242 let mut val = record!("a" => Value::test_int(1)).into_value(span);
5243 let res = val.insert_data_at_cell_path(
5244 &[PathMember::test_string(
5245 "b".into(),
5246 false,
5247 Casing::Sensitive,
5248 )],
5249 Value::test_int(2),
5250 span,
5251 );
5252 assert_eq!(res, Ok(()));
5253 assert_eq!(
5254 val,
5255 record!("a" => Value::test_int(1), "b" => Value::test_int(2)).into_value(span)
5256 );
5257 }
5258
5259 #[test]
5260 fn insert_existing_record_field_errors() {
5261 let span = Span::test_data();
5262 let mut val = record!("a" => Value::test_int(1)).into_value(span);
5263 let res = val.insert_data_at_cell_path(
5264 &[PathMember::test_string(
5265 "a".into(),
5266 false,
5267 Casing::Sensitive,
5268 )],
5269 Value::test_int(2),
5270 span,
5271 );
5272 assert!(matches!(res, Err(ShellError::ColumnAlreadyExists { .. })));
5273 }
5274
5275 #[test]
5276 fn insert_at_existing_list_index_shifts() {
5277 let mut val = Value::test_list(vec![Value::test_int(1), Value::test_int(2)]);
5278 let span = Span::test_data();
5279 let res = val.insert_data_at_cell_path(
5280 &[PathMember::test_int(0, false)],
5281 Value::test_int(99),
5282 span,
5283 );
5284 assert_eq!(res, Ok(()));
5285 assert_eq!(
5286 val,
5287 Value::test_list(vec![
5288 Value::test_int(99),
5289 Value::test_int(1),
5290 Value::test_int(2),
5291 ])
5292 );
5293 }
5294
5295 #[test]
5296 fn insert_appends_at_end_of_list() {
5297 let mut val = Value::test_list(vec![Value::test_int(1)]);
5298 let span = Span::test_data();
5299 let res = val.insert_data_at_cell_path(
5300 &[PathMember::test_int(1, false)],
5301 Value::test_int(2),
5302 span,
5303 );
5304 assert_eq!(res, Ok(()));
5305 assert_eq!(
5306 val,
5307 Value::test_list(vec![Value::test_int(1), Value::test_int(2)])
5308 );
5309 }
5310
5311 #[test]
5312 fn insert_beyond_end_errors() {
5313 let mut val = Value::test_list(vec![Value::test_int(1)]);
5314 let span = Span::test_data();
5315 let res = val.insert_data_at_cell_path(
5316 &[PathMember::test_int(5, false)],
5317 Value::test_int(2),
5318 span,
5319 );
5320 assert!(matches!(
5321 res,
5322 Err(ShellError::InsertAfterNextFreeIndex { .. })
5323 ));
5324 }
5325
5326 #[test]
5327 fn insert_existing_column_in_table_errors() {
5328 let span = Span::test_data();
5329 let mut val =
5330 Value::test_list(vec![record!("x" => Value::test_int(1)).into_value(span)]);
5331 let res = val.insert_data_at_cell_path(
5332 &[PathMember::test_string(
5333 "x".into(),
5334 false,
5335 Casing::Sensitive,
5336 )],
5337 Value::test_int(0),
5338 span,
5339 );
5340 assert!(matches!(res, Err(ShellError::ColumnAlreadyExists { .. })));
5341 }
5342
5343 #[test]
5344 fn insert_new_column_in_table() {
5345 let span = Span::test_data();
5346 let mut val =
5347 Value::test_list(vec![record!("x" => Value::test_int(1)).into_value(span)]);
5348 let res = val.insert_data_at_cell_path(
5349 &[PathMember::test_string(
5350 "y".into(),
5351 false,
5352 Casing::Sensitive,
5353 )],
5354 Value::test_int(2),
5355 span,
5356 );
5357 assert_eq!(res, Ok(()));
5358 assert_eq!(
5359 val,
5360 Value::test_list(vec![
5361 record!("x" => Value::test_int(1), "y" => Value::test_int(2)).into_value(span),
5362 ])
5363 );
5364 }
5365 }
5366
5367 mod is_empty {
5368 use super::*;
5369
5370 #[test]
5371 fn test_string() {
5372 let value = Value::test_string("");
5373 assert!(value.is_empty());
5374 }
5375
5376 #[test]
5377 fn test_list() {
5378 let list_with_no_values = Value::test_list(vec![]);
5379 let list_with_one_empty_string = Value::test_list(vec![Value::test_string("")]);
5380
5381 assert!(list_with_no_values.is_empty());
5382 assert!(!list_with_one_empty_string.is_empty());
5383 }
5384
5385 #[test]
5386 fn test_record() {
5387 let no_columns_nor_cell_values = Value::test_record(Record::new());
5388
5389 let one_column_and_one_cell_value_with_empty_strings = Value::test_record(record! {
5390 "" => Value::test_string(""),
5391 });
5392
5393 let one_column_with_a_string_and_one_cell_value_with_empty_string =
5394 Value::test_record(record! {
5395 "column" => Value::test_string(""),
5396 });
5397
5398 let one_column_with_empty_string_and_one_value_with_a_string =
5399 Value::test_record(record! {
5400 "" => Value::test_string("text"),
5401 });
5402
5403 assert!(no_columns_nor_cell_values.is_empty());
5404 assert!(!one_column_and_one_cell_value_with_empty_strings.is_empty());
5405 assert!(!one_column_with_a_string_and_one_cell_value_with_empty_string.is_empty());
5406 assert!(!one_column_with_empty_string_and_one_value_with_a_string.is_empty());
5407 }
5408 }
5409
5410 mod get_type {
5411 use crate::Type;
5412
5413 use super::*;
5414
5415 #[test]
5416 fn test_list() {
5417 let list_of_ints = Value::test_list(vec![Value::test_int(0)]);
5418 let list_of_floats = Value::test_list(vec![Value::test_float(0.0)]);
5419 let list_of_ints_and_floats =
5420 Value::test_list(vec![Value::test_int(0), Value::test_float(0.0)]);
5421 let list_of_ints_and_floats_and_bools = Value::test_list(vec![
5422 Value::test_int(0),
5423 Value::test_float(0.0),
5424 Value::test_bool(false),
5425 ]);
5426 assert_eq!(list_of_ints.get_type(), Type::List(Box::new(Type::Int)));
5427 assert_eq!(list_of_floats.get_type(), Type::List(Box::new(Type::Float)));
5428 assert_eq!(
5429 list_of_ints_and_floats_and_bools.get_type(),
5430 Type::List(Box::new(Type::one_of([Type::Number, Type::Bool])))
5431 );
5432 assert_eq!(
5433 list_of_ints_and_floats.get_type(),
5434 Type::List(Box::new(Type::Number))
5435 );
5436 }
5437 }
5438
5439 mod is_subtype {
5440 use crate::{CompareTypes, Type};
5441
5442 use super::*;
5443
5444 #[track_caller]
5445 fn assert_subtype_equivalent(value: &Value, ty: &Type) {
5446 assert_eq!(value.is_subtype_of(ty), value.get_type().is_subtype_of(ty));
5447 }
5448
5449 #[test]
5450 fn test_list() {
5451 let ty_int_list = Type::list(Type::Int);
5452 let ty_str_list = Type::list(Type::String);
5453 let ty_any_list = Type::list(Type::Any);
5454 let ty_list_list_int = Type::list(Type::list(Type::Int));
5455
5456 let list = Value::test_list(vec![
5457 Value::test_int(1),
5458 Value::test_int(2),
5459 Value::test_int(3),
5460 ]);
5461
5462 assert_subtype_equivalent(&list, &ty_int_list);
5463 assert_subtype_equivalent(&list, &ty_str_list);
5464 assert_subtype_equivalent(&list, &ty_any_list);
5465
5466 let list = Value::test_list(vec![
5467 Value::test_int(1),
5468 Value::test_string("hi"),
5469 Value::test_int(3),
5470 ]);
5471
5472 assert_subtype_equivalent(&list, &ty_int_list);
5473 assert_subtype_equivalent(&list, &ty_str_list);
5474 assert_subtype_equivalent(&list, &ty_any_list);
5475
5476 let list = Value::test_list(vec![Value::test_list(vec![Value::test_int(1)])]);
5477
5478 assert_subtype_equivalent(&list, &ty_list_list_int);
5479
5480 let ty_table = {
5482 Type::Table(
5483 vec![
5484 ("a".into(), Type::Int),
5485 ("b".into(), Type::Int),
5486 ("c".into(), Type::Int),
5487 ]
5488 .into(),
5489 )
5490 };
5491 let empty = Value::test_list(vec![]);
5492
5493 assert_subtype_equivalent(&empty, &ty_any_list);
5494 assert!(empty.is_subtype_of(&ty_int_list));
5495 assert!(empty.is_subtype_of(&ty_table));
5496 }
5497
5498 #[test]
5499 fn test_record() {
5500 let ty_abc = {
5501 Type::Record(
5502 vec![
5503 ("a".into(), Type::Int),
5504 ("b".into(), Type::Int),
5505 ("c".into(), Type::Int),
5506 ]
5507 .into(),
5508 )
5509 };
5510 let ty_ab = Type::Record(vec![("a".into(), Type::Int), ("b".into(), Type::Int)].into());
5511 let ty_inner = Type::Record(vec![("inner".into(), ty_abc.clone())].into());
5512
5513 let record_abc = Value::test_record(record! {
5514 "a" => Value::test_int(1),
5515 "b" => Value::test_int(2),
5516 "c" => Value::test_int(3),
5517 });
5518 let record_ab = Value::test_record(record! {
5519 "a" => Value::test_int(1),
5520 "b" => Value::test_int(2),
5521 });
5522
5523 assert_subtype_equivalent(&record_abc, &ty_abc);
5524 assert_subtype_equivalent(&record_abc, &ty_ab);
5525 assert_subtype_equivalent(&record_ab, &ty_abc);
5526 assert_subtype_equivalent(&record_ab, &ty_ab);
5527
5528 let record_inner = Value::test_record(record! {
5529 "inner" => record_abc
5530 });
5531 assert_subtype_equivalent(&record_inner, &ty_inner);
5532 }
5533
5534 #[test]
5535 fn test_table() {
5536 let ty_abc = Type::Table(
5537 vec![
5538 ("a".into(), Type::Int),
5539 ("b".into(), Type::Int),
5540 ("c".into(), Type::Int),
5541 ]
5542 .into(),
5543 );
5544 let ty_ab = Type::Table(vec![("a".into(), Type::Int), ("b".into(), Type::Int)].into());
5545 let ty_list_any = Type::list(Type::Any);
5546
5547 let record_abc = Value::test_record(record! {
5548 "a" => Value::test_int(1),
5549 "b" => Value::test_int(2),
5550 "c" => Value::test_int(3),
5551 });
5552 let record_ab = Value::test_record(record! {
5553 "a" => Value::test_int(1),
5554 "b" => Value::test_int(2),
5555 });
5556
5557 let table_abc = Value::test_list(vec![record_abc.clone(), record_abc.clone()]);
5558 let table_ab = Value::test_list(vec![record_ab.clone(), record_ab.clone()]);
5559
5560 assert_subtype_equivalent(&table_abc, &ty_abc);
5561 assert_subtype_equivalent(&table_abc, &ty_ab);
5562 assert_subtype_equivalent(&table_ab, &ty_abc);
5563 assert_subtype_equivalent(&table_ab, &ty_ab);
5564 assert_subtype_equivalent(&table_abc, &ty_list_any);
5565
5566 let table_mixed = Value::test_list(vec![record_abc.clone(), record_ab.clone()]);
5567 assert_subtype_equivalent(&table_mixed, &ty_abc);
5568 assert!(table_mixed.is_subtype_of(&ty_ab));
5569
5570 let ty_a = Type::Table(vec![("a".into(), Type::Any)].into());
5571 let table_mixed_types = Value::test_list(vec![
5572 Value::test_record(record! {
5573 "a" => Value::test_int(1),
5574 }),
5575 Value::test_record(record! {
5576 "a" => Value::test_string("a"),
5577 }),
5578 ]);
5579 assert!(table_mixed_types.is_subtype_of(&ty_a));
5580 }
5581 }
5582
5583 mod into_string {
5584 use chrono::{DateTime, FixedOffset};
5585
5586 use super::*;
5587
5588 #[test]
5589 fn test_datetime() {
5590 let date = DateTime::from_timestamp_millis(-123456789)
5591 .unwrap()
5592 .with_timezone(&FixedOffset::east_opt(0).unwrap());
5593
5594 let string = Value::test_date(date).to_expanded_string("", &Default::default());
5595
5596 let formatted = string.split('(').next().unwrap();
5599 assert_eq!("Tue, 30 Dec 1969 13:42:23 +0000 ", formatted);
5600 }
5601
5602 #[test]
5603 fn test_negative_year_datetime() {
5604 let date = DateTime::from_timestamp_millis(-72135596800000)
5605 .unwrap()
5606 .with_timezone(&FixedOffset::east_opt(0).unwrap());
5607
5608 let string = Value::test_date(date).to_expanded_string("", &Default::default());
5609
5610 let formatted = string.split(' ').next().unwrap();
5613 assert_eq!("-0316-02-11T06:13:20+00:00", formatted);
5614 }
5615 }
5616
5617 #[test]
5618 fn test_env_as_bool() {
5619 assert_eq!(Value::test_bool(false).coerce_bool(), Ok(false));
5621 assert_eq!(Value::test_int(0).coerce_bool(), Ok(false));
5622 assert_eq!(Value::test_float(0.0).coerce_bool(), Ok(false));
5623 assert_eq!(Value::test_string("").coerce_bool(), Ok(false));
5624 assert_eq!(Value::test_string("0").coerce_bool(), Ok(false));
5625 assert_eq!(Value::test_nothing().coerce_bool(), Ok(false));
5626
5627 assert_eq!(Value::test_bool(true).coerce_bool(), Ok(true));
5629 assert_eq!(Value::test_int(1).coerce_bool(), Ok(true));
5630 assert_eq!(Value::test_float(1.0).coerce_bool(), Ok(true));
5631 assert_eq!(Value::test_string("1").coerce_bool(), Ok(true));
5632
5633 assert_eq!(Value::test_int(42).coerce_bool(), Ok(true));
5635 assert_eq!(Value::test_float(0.5).coerce_bool(), Ok(true));
5636 assert_eq!(Value::test_string("not zero").coerce_bool(), Ok(true));
5637
5638 assert!(Value::test_record(Record::default()).coerce_bool().is_err());
5640 assert!(
5641 Value::test_list(vec![Value::test_int(1)])
5642 .coerce_bool()
5643 .is_err()
5644 );
5645 assert!(
5646 Value::test_date(
5647 chrono::DateTime::parse_from_rfc3339("2024-01-01T12:00:00+00:00").unwrap(),
5648 )
5649 .coerce_bool()
5650 .is_err()
5651 );
5652 assert!(Value::test_glob("*.rs").coerce_bool().is_err());
5653 assert!(Value::test_binary(vec![1, 2, 3]).coerce_bool().is_err());
5654 assert!(Value::test_duration(3600).coerce_bool().is_err());
5655 }
5656
5657 mod memory_size {
5658 use super::*;
5659
5660 #[test]
5661 fn test_primitive_sizes() {
5662 let base_size = std::mem::size_of::<Value>();
5664
5665 assert_eq!(Value::test_bool(true).memory_size(), base_size);
5666 assert_eq!(Value::test_int(42).memory_size(), base_size);
5667 assert_eq!(Value::test_float(1.5).memory_size(), base_size);
5668 assert_eq!(Value::test_nothing().memory_size(), base_size);
5669 }
5670
5671 #[test]
5672 fn test_string_size() {
5673 let s = "hello world";
5674 let val = Value::test_string(s);
5675 let base_size = std::mem::size_of::<Value>();
5676 let string_val = String::from(s);
5678 let expected = base_size + string_val.capacity();
5679 assert_eq!(val.memory_size(), expected);
5680 }
5681
5682 #[test]
5683 fn test_binary_size() {
5684 let data = vec![1, 2, 3, 4, 5];
5685 let val = Value::test_binary(data.clone());
5686 let base_size = std::mem::size_of::<Value>();
5687 let expected = base_size + data.capacity();
5688 assert_eq!(val.memory_size(), expected);
5689 }
5690
5691 #[test]
5692 fn test_list_size() {
5693 let list = Value::test_list(vec![
5694 Value::test_int(1),
5695 Value::test_int(2),
5696 Value::test_int(3),
5697 ]);
5698
5699 let base_size = std::mem::size_of::<Value>();
5700 let element_size = std::mem::size_of::<Value>();
5701 let expected = base_size + 3 * element_size;
5703 assert_eq!(list.memory_size(), expected);
5704 }
5705
5706 #[test]
5707 fn test_record_size() {
5708 let record = Value::test_record(record! {
5709 "a" => Value::test_int(1),
5710 "b" => Value::test_string("hello"),
5711 });
5712
5713 let base_size = std::mem::size_of::<Value>();
5714 let record_base_size = std::mem::size_of::<Record>();
5715 let key1_size = String::from("a").capacity();
5716 let key2_size = String::from("b").capacity();
5717 let val1_size = std::mem::size_of::<Value>();
5718 let val2_base_size = std::mem::size_of::<Value>();
5719 let val2_string_size = String::from("hello").capacity();
5720
5721 let expected = base_size
5722 + record_base_size
5723 + key1_size
5724 + key2_size
5725 + val1_size
5726 + (val2_base_size + val2_string_size);
5727 assert_eq!(record.memory_size(), expected);
5728 }
5729
5730 #[test]
5731 fn test_nested_structure_size() {
5732 let inner_record = Value::test_record(record! {
5734 "x" => Value::test_int(10),
5735 "y" => Value::test_string("test"),
5736 });
5737
5738 let list = Value::test_list(vec![inner_record]);
5739
5740 let record_size = list.memory_size();
5741 let base_size = std::mem::size_of::<Value>();
5743 assert!(record_size > base_size);
5744
5745 let simple_list = Value::test_list(vec![Value::test_int(1)]);
5747 assert!(record_size > simple_list.memory_size());
5748 }
5749 }
5750}