nu_protocol/value/
from_value.rs

1use crate::{
2    NuGlob, Range, Record, ShellError, Span, Spanned, Type, Value,
3    ast::{CellPath, PathMember},
4    casing::Casing,
5    engine::Closure,
6};
7use chrono::{DateTime, FixedOffset};
8use std::{
9    any,
10    cmp::Ordering,
11    collections::{HashMap, VecDeque},
12    fmt,
13    path::PathBuf,
14    str::FromStr,
15};
16
17/// A trait for loading a value from a [`Value`].
18///
19/// # Derivable
20/// This trait can be used with `#[derive]`.
21///
22/// When derived on structs with named fields, it expects a [`Value::Record`] where each field of
23/// the struct maps to a corresponding field in the record.
24///
25/// - If `#[nu_value(rename = "...")]` is applied to a field, that name will be used as the key in
26///   the record.
27/// - If `#[nu_value(rename_all = "...")]` is applied on the container (struct) the key of the
28///   field will be case-converted accordingly.
29/// - If neither attribute is applied, the field name is used as is.
30/// - If `#[nu_value(default)]` is applied to a field, the field type's [`Default`] implementation
31///   will be used if the corresponding record field is missing
32///
33/// Supported case conversions include those provided by [`heck`], such as
34/// "snake_case", "kebab-case", "PascalCase", and others.
35/// Additionally, all values accepted by
36/// [`#[serde(rename_all = "...")]`](https://serde.rs/container-attrs.html#rename_all) are valid here.
37///
38/// For structs with unnamed fields, it expects a [`Value::List`], and the fields are populated in
39/// the order they appear in the list.
40/// Unit structs expect a [`Value::Nothing`], as they contain no data.
41/// Attempting to convert from a non-matching `Value` type will result in an error.
42///
43/// Only enums with no fields may derive this trait.
44/// The expected value representation will be the name of the variant as a [`Value::String`].
45///
46/// - If `#[nu_value(rename = "...")]` is applied to a variant, that name will be used.
47/// - If `#[nu_value(rename_all = "...")]` is applied on the enum container, the name of variant
48///   will be case-converted accordingly.
49/// - If neither attribute is applied, the variant name will default to
50///   ["snake_case"](heck::ToSnakeCase).
51///
52/// Additionally, you can use `#[nu_value(type_name = "...")]` in the derive macro to set a custom type name
53/// for `FromValue::expected_type`. This will result in a `Type::Custom` with the specified type name.
54/// This can be useful in situations where the default type name is not desired.
55///
56/// # Enum Example
57/// ```
58/// # use nu_protocol::{FromValue, Value, ShellError, record, Span};
59/// #
60/// # let span = Span::unknown();
61/// #
62/// #[derive(FromValue, Debug, PartialEq)]
63/// #[nu_value(rename_all = "COBOL-CASE", type_name = "birb")]
64/// enum Bird {
65///     MountainEagle,
66///     ForestOwl,
67///     #[nu_value(rename = "RIVER-QUACK")]
68///     RiverDuck,
69/// }
70///
71/// assert_eq!(
72///     Bird::from_value(Value::string("FOREST-OWL", span)).unwrap(),
73///     Bird::ForestOwl
74/// );
75///
76/// assert_eq!(
77///     Bird::from_value(Value::string("RIVER-QUACK", span)).unwrap(),
78///     Bird::RiverDuck
79/// );
80///
81/// assert_eq!(
82///     &Bird::expected_type().to_string(),
83///     "birb"
84/// );
85/// ```
86///
87/// # Struct Example
88/// ```
89/// # use nu_protocol::{FromValue, Value, ShellError, record, Span};
90/// #
91/// # let span = Span::unknown();
92/// #
93/// #[derive(FromValue, PartialEq, Eq, Debug)]
94/// #[nu_value(rename_all = "kebab-case")]
95/// struct Person {
96///     first_name: String,
97///     last_name: String,
98///     #[nu_value(rename = "age")]
99///     age_years: u32,
100/// }
101///
102/// let value = Value::record(record! {
103///     "first-name" => Value::string("John", span),
104///     "last-name" => Value::string("Doe", span),
105///     "age" => Value::int(42, span),
106/// }, span);
107///
108/// assert_eq!(
109///     Person::from_value(value).unwrap(),
110///     Person {
111///         first_name: "John".into(),
112///         last_name: "Doe".into(),
113///         age_years: 42,
114///     }
115/// );
116/// ```
117pub trait FromValue: Sized {
118    // TODO: instead of ShellError, maybe we could have a FromValueError that implements Into<ShellError>
119    /// Loads a value from a [`Value`].
120    ///
121    /// This method retrieves a value similarly to how strings are parsed using [`FromStr`].
122    /// The operation might fail if the `Value` contains unexpected types or structures.
123    fn from_value(v: Value) -> Result<Self, ShellError>;
124
125    /// Expected `Value` type.
126    ///
127    /// This is used to print out errors of what type of value is expected for conversion.
128    /// Even if not used in [`from_value`](FromValue::from_value) this should still be implemented
129    /// so that other implementations like `Option` or `Vec` can make use of it.
130    /// It is advised to call this method in `from_value` to ensure that expected type in the error
131    /// is consistent.
132    ///
133    /// Unlike the default implementation, derived implementations explicitly reveal the concrete
134    /// type, such as [`Type::Record`] or [`Type::List`], instead of an opaque type.
135    fn expected_type() -> Type {
136        Type::Custom(
137            any::type_name::<Self>()
138                .split(':')
139                .next_back()
140                .expect("str::split returns an iterator with at least one element")
141                .to_string()
142                .into_boxed_str(),
143        )
144    }
145}
146
147// Primitive Types
148
149impl<T, const N: usize> FromValue for [T; N]
150where
151    T: FromValue,
152{
153    fn from_value(v: Value) -> Result<Self, ShellError> {
154        let span = v.span();
155        let v_ty = v.get_type();
156        let vec = Vec::<T>::from_value(v)?;
157        vec.try_into()
158            .map_err(|err_vec: Vec<T>| ShellError::CantConvert {
159                to_type: Self::expected_type().to_string(),
160                from_type: v_ty.to_string(),
161                span,
162                help: Some(match err_vec.len().cmp(&N) {
163                    Ordering::Less => format!(
164                        "input list too short ({}), expected length of {N}, add missing values",
165                        err_vec.len()
166                    ),
167                    Ordering::Equal => {
168                        unreachable!("conversion would have worked if the length would be the same")
169                    }
170                    Ordering::Greater => format!(
171                        "input list too long ({}), expected length of {N}, remove trailing values",
172                        err_vec.len()
173                    ),
174                }),
175            })
176    }
177
178    fn expected_type() -> Type {
179        Type::Custom(format!("list<{};{N}>", T::expected_type()).into_boxed_str())
180    }
181}
182
183impl FromValue for bool {
184    fn from_value(v: Value) -> Result<Self, ShellError> {
185        match v {
186            Value::Bool { val, .. } => Ok(val),
187            v => Err(ShellError::CantConvert {
188                to_type: Self::expected_type().to_string(),
189                from_type: v.get_type().to_string(),
190                span: v.span(),
191                help: None,
192            }),
193        }
194    }
195
196    fn expected_type() -> Type {
197        Type::Bool
198    }
199}
200
201impl FromValue for char {
202    fn from_value(v: Value) -> Result<Self, ShellError> {
203        let span = v.span();
204        let v_ty = v.get_type();
205        match v {
206            Value::String { ref val, .. } => match char::from_str(val) {
207                Ok(c) => Ok(c),
208                Err(_) => Err(ShellError::CantConvert {
209                    to_type: Self::expected_type().to_string(),
210                    from_type: v_ty.to_string(),
211                    span,
212                    help: Some("make the string only one char long".to_string()),
213                }),
214            },
215            _ => Err(ShellError::CantConvert {
216                to_type: Self::expected_type().to_string(),
217                from_type: v_ty.to_string(),
218                span,
219                help: None,
220            }),
221        }
222    }
223
224    fn expected_type() -> Type {
225        Type::String
226    }
227}
228
229impl FromValue for f32 {
230    fn from_value(v: Value) -> Result<Self, ShellError> {
231        f64::from_value(v).map(|float| float as f32)
232    }
233}
234
235impl FromValue for f64 {
236    fn from_value(v: Value) -> Result<Self, ShellError> {
237        match v {
238            Value::Float { val, .. } => Ok(val),
239            Value::Int { val, .. } => Ok(val as f64),
240            v => Err(ShellError::CantConvert {
241                to_type: Self::expected_type().to_string(),
242                from_type: v.get_type().to_string(),
243                span: v.span(),
244                help: None,
245            }),
246        }
247    }
248
249    fn expected_type() -> Type {
250        Type::Float
251    }
252}
253
254impl FromValue for i64 {
255    fn from_value(v: Value) -> Result<Self, ShellError> {
256        match v {
257            Value::Int { val, .. } => Ok(val),
258            Value::Duration { val, .. } => Ok(val),
259            v => Err(ShellError::CantConvert {
260                to_type: Self::expected_type().to_string(),
261                from_type: v.get_type().to_string(),
262                span: v.span(),
263                help: None,
264            }),
265        }
266    }
267
268    fn expected_type() -> Type {
269        Type::Int
270    }
271}
272
273macro_rules! impl_from_value_for_int {
274    ($type:ty) => {
275        impl FromValue for $type {
276            fn from_value(v: Value) -> Result<Self, ShellError> {
277                let span = v.span();
278                let int = i64::from_value(v)?;
279                const MIN: i64 = <$type>::MIN as i64;
280                const MAX: i64 = <$type>::MAX as i64;
281                #[allow(overlapping_range_endpoints)] // calculating MIN-1 is not possible for i64::MIN
282                #[allow(unreachable_patterns)] // isize might max out i64 number range
283                <$type>::try_from(int).map_err(|_| match int {
284                    MIN..=MAX => unreachable!(
285                        "int should be within the valid range for {}",
286                        stringify!($type)
287                    ),
288                    i64::MIN..=MIN => int_too_small_error(int, <$type>::MIN, span),
289                    MAX..=i64::MAX => int_too_large_error(int, <$type>::MAX, span),
290                })
291            }
292
293            fn expected_type() -> Type {
294                i64::expected_type()
295            }
296        }
297    };
298}
299
300impl_from_value_for_int!(i8);
301impl_from_value_for_int!(i16);
302impl_from_value_for_int!(i32);
303impl_from_value_for_int!(isize);
304
305macro_rules! impl_from_value_for_uint {
306    ($type:ty, $max:expr) => {
307        impl FromValue for $type {
308            fn from_value(v: Value) -> Result<Self, ShellError> {
309                let span = v.span();
310                const MAX: i64 = $max;
311                match v {
312                    Value::Int { val, .. } | Value::Duration { val, .. } => {
313                        match val {
314                            i64::MIN..=-1 => Err(ShellError::NeedsPositiveValue { span }),
315                            0..=MAX => Ok(val as $type),
316                            #[allow(unreachable_patterns)] // u64 will max out the i64 number range
317                            n => Err(ShellError::GenericError {
318                                error: "Integer too large".to_string(),
319                                msg: format!("{n} is larger than {MAX}"),
320                                span: Some(span),
321                                help: None,
322                                inner: vec![],
323                            }),
324                        }
325                    }
326                    v => Err(ShellError::CantConvert {
327                        to_type: Self::expected_type().to_string(),
328                        from_type: v.get_type().to_string(),
329                        span: v.span(),
330                        help: None,
331                    }),
332                }
333            }
334
335            fn expected_type() -> Type {
336                Type::Custom("non-negative int".to_string().into_boxed_str())
337            }
338        }
339    };
340}
341
342// Sadly we cannot implement FromValue for u8 without losing the impl of Vec<u8>,
343// Rust would find two possible implementations then, Vec<u8> and Vec<T = u8>,
344// and wouldn't compile.
345// The blanket implementation for Vec<T> is probably more useful than
346// implementing FromValue for u8.
347
348impl_from_value_for_uint!(u16, u16::MAX as i64);
349impl_from_value_for_uint!(u32, u32::MAX as i64);
350impl_from_value_for_uint!(u64, i64::MAX); // u64::Max would be -1 as i64
351#[cfg(target_pointer_width = "64")]
352impl_from_value_for_uint!(usize, i64::MAX);
353#[cfg(target_pointer_width = "32")]
354impl_from_value_for_uint!(usize, usize::MAX as i64);
355
356impl FromValue for () {
357    fn from_value(v: Value) -> Result<Self, ShellError> {
358        match v {
359            Value::Nothing { .. } => Ok(()),
360            v => Err(ShellError::CantConvert {
361                to_type: Self::expected_type().to_string(),
362                from_type: v.get_type().to_string(),
363                span: v.span(),
364                help: None,
365            }),
366        }
367    }
368
369    fn expected_type() -> Type {
370        Type::Nothing
371    }
372}
373
374macro_rules! tuple_from_value {
375    ($template:literal, $($t:ident:$n:tt),+) => {
376        impl<$($t),+> FromValue for ($($t,)+) where $($t: FromValue,)+ {
377            fn from_value(v: Value) -> Result<Self, ShellError> {
378                let span = v.span();
379                match v {
380                    Value::List { vals, .. } => {
381                        let mut deque = VecDeque::from(vals);
382
383                        Ok(($(
384                            {
385                                let v = deque.pop_front().ok_or_else(|| ShellError::CantFindColumn {
386                                    col_name: $n.to_string(),
387                                    span: None,
388                                    src_span: span
389                                })?;
390                                $t::from_value(v)?
391                            },
392                        )*))
393                    },
394                    v => Err(ShellError::CantConvert {
395                        to_type: Self::expected_type().to_string(),
396                        from_type: v.get_type().to_string(),
397                        span: v.span(),
398                        help: None,
399                    }),
400                }
401            }
402
403            fn expected_type() -> Type {
404                Type::Custom(
405                    format!(
406                        $template,
407                        $($t::expected_type()),*
408                    )
409                    .into_boxed_str(),
410                )
411            }
412        }
413    };
414}
415
416// Tuples in std are implemented for up to 12 elements, so we do it here too.
417tuple_from_value!("[{}]", T0:0);
418tuple_from_value!("[{}, {}]", T0:0, T1:1);
419tuple_from_value!("[{}, {}, {}]", T0:0, T1:1, T2:2);
420tuple_from_value!("[{}, {}, {}, {}]", T0:0, T1:1, T2:2, T3:3);
421tuple_from_value!("[{}, {}, {}, {}, {}]", T0:0, T1:1, T2:2, T3:3, T4:4);
422tuple_from_value!("[{}, {}, {}, {}, {}, {}]", T0:0, T1:1, T2:2, T3:3, T4:4, T5:5);
423tuple_from_value!("[{}, {}, {}, {}, {}, {}, {}]", T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6);
424tuple_from_value!("[{}, {}, {}, {}, {}, {}, {}, {}]", T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7);
425tuple_from_value!("[{}, {}, {}, {}, {}, {}, {}, {}, {}]", T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7, T8:8);
426tuple_from_value!("[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}]", T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7, T8:8, T9:9);
427tuple_from_value!("[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}]", T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7, T8:8, T9:9, T10:10);
428tuple_from_value!("[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}]", T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7, T8:8, T9:9, T10:10, T11:11);
429
430// Other std Types
431
432impl FromValue for PathBuf {
433    fn from_value(v: Value) -> Result<Self, ShellError> {
434        match v {
435            Value::String { val, .. } => Ok(val.into()),
436            v => Err(ShellError::CantConvert {
437                to_type: Self::expected_type().to_string(),
438                from_type: v.get_type().to_string(),
439                span: v.span(),
440                help: None,
441            }),
442        }
443    }
444
445    fn expected_type() -> Type {
446        Type::String
447    }
448}
449
450impl FromValue for String {
451    fn from_value(v: Value) -> Result<Self, ShellError> {
452        // FIXME: we may want to fail a little nicer here
453        match v {
454            Value::CellPath { val, .. } => Ok(val.to_string()),
455            Value::String { val, .. } => Ok(val),
456            v => Err(ShellError::CantConvert {
457                to_type: Self::expected_type().to_string(),
458                from_type: v.get_type().to_string(),
459                span: v.span(),
460                help: None,
461            }),
462        }
463    }
464
465    fn expected_type() -> Type {
466        Type::String
467    }
468}
469
470// This impl is different from Vec<T> as it allows reading from Value::Binary and Value::String too.
471// This also denies implementing FromValue for u8 as it would be in conflict with the Vec<T> impl.
472impl FromValue for Vec<u8> {
473    fn from_value(v: Value) -> Result<Self, ShellError> {
474        match v {
475            Value::Binary { val, .. } => Ok(val),
476            Value::String { val, .. } => Ok(val.into_bytes()),
477            Value::List { vals, .. } => {
478                const U8MIN: i64 = u8::MIN as i64;
479                const U8MAX: i64 = u8::MAX as i64;
480                let mut this = Vec::with_capacity(vals.len());
481                for val in vals {
482                    let span = val.span();
483                    let int = i64::from_value(val)?;
484                    // calculating -1 on these ranges would be less readable
485                    #[allow(overlapping_range_endpoints)]
486                    #[allow(clippy::match_overlapping_arm)]
487                    match int {
488                        U8MIN..=U8MAX => this.push(int as u8),
489                        i64::MIN..=U8MIN => return Err(int_too_small_error(int, U8MIN, span)),
490                        U8MAX..=i64::MAX => return Err(int_too_large_error(int, U8MAX, span)),
491                    };
492                }
493                Ok(this)
494            }
495            v => Err(ShellError::CantConvert {
496                to_type: Self::expected_type().to_string(),
497                from_type: v.get_type().to_string(),
498                span: v.span(),
499                help: None,
500            }),
501        }
502    }
503
504    fn expected_type() -> Type {
505        Type::Binary
506    }
507}
508
509// Blanket std Implementations
510
511impl<T> FromValue for Option<T>
512where
513    T: FromValue,
514{
515    fn from_value(v: Value) -> Result<Self, ShellError> {
516        match v {
517            Value::Nothing { .. } => Ok(None),
518            v => T::from_value(v).map(Option::Some),
519        }
520    }
521
522    fn expected_type() -> Type {
523        T::expected_type()
524    }
525}
526
527impl<V> FromValue for HashMap<String, V>
528where
529    V: FromValue,
530{
531    fn from_value(v: Value) -> Result<Self, ShellError> {
532        let record = v.into_record()?;
533        let items: Result<Vec<(String, V)>, ShellError> = record
534            .into_iter()
535            .map(|(k, v)| Ok((k, V::from_value(v)?)))
536            .collect();
537        Ok(HashMap::from_iter(items?))
538    }
539
540    fn expected_type() -> Type {
541        Type::Record(vec![].into_boxed_slice())
542    }
543}
544
545impl<T> FromValue for Vec<T>
546where
547    T: FromValue,
548{
549    fn from_value(v: Value) -> Result<Self, ShellError> {
550        match v {
551            Value::List { vals, .. } => vals
552                .into_iter()
553                .map(|v| T::from_value(v))
554                .collect::<Result<Vec<T>, ShellError>>(),
555            v => Err(ShellError::CantConvert {
556                to_type: Self::expected_type().to_string(),
557                from_type: v.get_type().to_string(),
558                span: v.span(),
559                help: None,
560            }),
561        }
562    }
563
564    fn expected_type() -> Type {
565        Type::List(Box::new(T::expected_type()))
566    }
567}
568
569// Nu Types
570
571impl FromValue for Value {
572    fn from_value(v: Value) -> Result<Self, ShellError> {
573        Ok(v)
574    }
575
576    fn expected_type() -> Type {
577        Type::Any
578    }
579}
580
581impl FromValue for CellPath {
582    fn from_value(v: Value) -> Result<Self, ShellError> {
583        let span = v.span();
584        match v {
585            Value::CellPath { val, .. } => Ok(val),
586            Value::String { val, .. } => Ok(CellPath {
587                members: vec![PathMember::String {
588                    val,
589                    span,
590                    optional: false,
591                    casing: Casing::Sensitive,
592                }],
593            }),
594            Value::Int { val, .. } => {
595                if val.is_negative() {
596                    Err(ShellError::NeedsPositiveValue { span })
597                } else {
598                    Ok(CellPath {
599                        members: vec![PathMember::Int {
600                            val: val as usize,
601                            span,
602                            optional: false,
603                        }],
604                    })
605                }
606            }
607            x => Err(ShellError::CantConvert {
608                to_type: Self::expected_type().to_string(),
609                from_type: x.get_type().to_string(),
610                span,
611                help: None,
612            }),
613        }
614    }
615
616    fn expected_type() -> Type {
617        Type::CellPath
618    }
619}
620
621impl FromValue for Closure {
622    fn from_value(v: Value) -> Result<Self, ShellError> {
623        match v {
624            Value::Closure { val, .. } => Ok(*val),
625            v => Err(ShellError::CantConvert {
626                to_type: Self::expected_type().to_string(),
627                from_type: v.get_type().to_string(),
628                span: v.span(),
629                help: None,
630            }),
631        }
632    }
633}
634
635impl FromValue for DateTime<FixedOffset> {
636    fn from_value(v: Value) -> Result<Self, ShellError> {
637        match v {
638            Value::Date { val, .. } => Ok(val),
639            v => Err(ShellError::CantConvert {
640                to_type: Self::expected_type().to_string(),
641                from_type: v.get_type().to_string(),
642                span: v.span(),
643                help: None,
644            }),
645        }
646    }
647
648    fn expected_type() -> Type {
649        Type::Date
650    }
651}
652
653impl FromValue for NuGlob {
654    fn from_value(v: Value) -> Result<Self, ShellError> {
655        // FIXME: we may want to fail a little nicer here
656        match v {
657            Value::CellPath { val, .. } => Ok(NuGlob::Expand(val.to_string())),
658            Value::String { val, .. } => Ok(NuGlob::DoNotExpand(val)),
659            Value::Glob {
660                val,
661                no_expand: quoted,
662                ..
663            } => {
664                if quoted {
665                    Ok(NuGlob::DoNotExpand(val))
666                } else {
667                    Ok(NuGlob::Expand(val))
668                }
669            }
670            v => Err(ShellError::CantConvert {
671                to_type: Self::expected_type().to_string(),
672                from_type: v.get_type().to_string(),
673                span: v.span(),
674                help: None,
675            }),
676        }
677    }
678
679    fn expected_type() -> Type {
680        Type::String
681    }
682}
683
684impl FromValue for Range {
685    fn from_value(v: Value) -> Result<Self, ShellError> {
686        match v {
687            Value::Range { val, .. } => Ok(*val),
688            v => Err(ShellError::CantConvert {
689                to_type: Self::expected_type().to_string(),
690                from_type: v.get_type().to_string(),
691                span: v.span(),
692                help: None,
693            }),
694        }
695    }
696
697    fn expected_type() -> Type {
698        Type::Range
699    }
700}
701
702impl FromValue for Record {
703    fn from_value(v: Value) -> Result<Self, ShellError> {
704        match v {
705            Value::Record { val, .. } => Ok(val.into_owned()),
706            v => Err(ShellError::CantConvert {
707                to_type: Self::expected_type().to_string(),
708                from_type: v.get_type().to_string(),
709                span: v.span(),
710                help: None,
711            }),
712        }
713    }
714}
715
716// Blanket Nu Implementations
717
718impl<T> FromValue for Spanned<T>
719where
720    T: FromValue,
721{
722    fn from_value(v: Value) -> Result<Self, ShellError> {
723        let span = v.span();
724        Ok(Spanned {
725            item: T::from_value(v)?,
726            span,
727        })
728    }
729
730    fn expected_type() -> Type {
731        T::expected_type()
732    }
733}
734
735// Foreign Types
736
737impl FromValue for bytes::Bytes {
738    fn from_value(v: Value) -> Result<Self, ShellError> {
739        match v {
740            Value::Binary { val, .. } => Ok(val.into()),
741            v => Err(ShellError::CantConvert {
742                to_type: Self::expected_type().to_string(),
743                from_type: v.get_type().to_string(),
744                span: v.span(),
745                help: None,
746            }),
747        }
748    }
749
750    fn expected_type() -> Type {
751        Type::Binary
752    }
753}
754
755// Use generics with `fmt::Display` to allow passing different kinds of integer
756fn int_too_small_error(int: impl fmt::Display, min: impl fmt::Display, span: Span) -> ShellError {
757    ShellError::GenericError {
758        error: "Integer too small".to_string(),
759        msg: format!("{int} is smaller than {min}"),
760        span: Some(span),
761        help: None,
762        inner: vec![],
763    }
764}
765
766fn int_too_large_error(int: impl fmt::Display, max: impl fmt::Display, span: Span) -> ShellError {
767    ShellError::GenericError {
768        error: "Integer too large".to_string(),
769        msg: format!("{int} is larger than {max}"),
770        span: Some(span),
771        help: None,
772        inner: vec![],
773    }
774}
775
776#[cfg(test)]
777mod tests {
778    use crate::{FromValue, IntoValue, Record, Span, Type, Value, engine::Closure};
779    use std::ops::Deref;
780
781    #[test]
782    fn expected_type_default_impl() {
783        assert_eq!(
784            Record::expected_type(),
785            Type::Custom("Record".to_string().into_boxed_str())
786        );
787
788        assert_eq!(
789            Closure::expected_type(),
790            Type::Custom("Closure".to_string().into_boxed_str())
791        );
792    }
793
794    #[test]
795    fn from_value_vec_u8() {
796        let vec: Vec<u8> = vec![1, 2, 3];
797        let span = Span::test_data();
798        let string = "Hello Vec<u8>!".to_string();
799
800        assert_eq!(
801            Vec::<u8>::from_value(vec.clone().into_value(span)).unwrap(),
802            vec.clone(),
803            "Vec<u8> roundtrip"
804        );
805
806        assert_eq!(
807            Vec::<u8>::from_value(Value::test_string(string.clone()))
808                .unwrap()
809                .deref(),
810            string.as_bytes(),
811            "Vec<u8> from String"
812        );
813
814        assert_eq!(
815            Vec::<u8>::from_value(Value::test_binary(vec.clone())).unwrap(),
816            vec,
817            "Vec<u8> from Binary"
818        );
819
820        assert!(Vec::<u8>::from_value(vec![u8::MIN as i32 - 1].into_value(span)).is_err());
821        assert!(Vec::<u8>::from_value(vec![u8::MAX as i32 + 1].into_value(span)).is_err());
822    }
823}