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