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