Skip to main content

nu_protocol/value/
into_value.rs

1use crate::{Range, Record, ShellError, Span, Value, ast::CellPath, engine::Closure};
2use chrono::{DateTime, FixedOffset};
3use nu_path::form::PathForm;
4use std::{
5    borrow::{Borrow, Cow},
6    collections::HashMap,
7};
8
9/// A trait for converting a value into a [`Value`].
10///
11/// This conversion is infallible, for fallible conversions use [`TryIntoValue`].
12///
13/// # Derivable
14/// This trait can be used with `#[derive]`.
15/// When derived on structs with named fields, the resulting value representation will use
16/// [`Value::Record`], where each field of the record corresponds to a field of the struct.
17///
18/// By default, field names will be used as-is unless specified otherwise:
19/// - If `#[nu_value(rename = "...")]` is applied to a specific field, that name is used.
20/// - If `#[nu_value(rename_all = "...")]` is applied to the struct, field names will be
21///   case-converted accordingly.
22/// - If neither attribute is used, the original field name will be retained.
23///
24/// For structs with unnamed fields, the value representation will be [`Value::List`], with all
25/// fields inserted into a list.
26/// Unit structs will be represented as [`Value::Nothing`] since they contain no data.
27///
28/// For enums, the resulting value representation depends on the variant name:
29/// - If `#[nu_value(rename = "...")]` is applied to a specific variant, that name is used.
30/// - If `#[nu_value(rename_all = "...")]` is applied to the enum, variant names will be
31///   case-converted accordingly.
32/// - If neither attribute is used, variant names will default to snake_case.
33///
34/// Only enums with no fields may derive this trait.
35/// The resulting value will be the name of the variant as a [`Value::String`].
36///
37/// All case options from [`heck`] are supported, as well as the values allowed by
38/// [`#[serde(rename_all)]`](https://serde.rs/container-attrs.html#rename_all).
39///
40/// # Enum Example
41/// ```
42/// # use nu_protocol::{IntoValue, Value, Span, record};
43/// #
44/// # let span = Span::unknown();
45/// #
46/// #[derive(IntoValue)]
47/// #[nu_value(rename_all = "COBOL-CASE")]
48/// enum Bird {
49///     MountainEagle,
50///     ForestOwl,
51///     #[nu_value(rename = "RIVER-QUACK")]
52///     RiverDuck,
53/// }
54///
55/// assert_eq!(
56///     Bird::ForestOwl.into_value(span),
57///     Value::string("FOREST-OWL", span)
58/// );
59///
60/// assert_eq!(
61///     Bird::RiverDuck.into_value(span),
62///     Value::string("RIVER-QUACK", span)
63/// );
64/// ```
65///
66/// # Struct Example
67/// ```
68/// # use nu_protocol::{IntoValue, Value, Span, record};
69/// #
70/// # let span = Span::unknown();
71/// #
72/// #[derive(IntoValue)]
73/// #[nu_value(rename_all = "kebab-case")]
74/// struct Person {
75///     first_name: String,
76///     last_name: String,
77///     #[nu_value(rename = "age")]
78///     age_years: u32,
79/// }
80///
81/// assert_eq!(
82///     Person {
83///         first_name: "John".into(),
84///         last_name: "Doe".into(),
85///         age_years: 42,
86///     }.into_value(span),
87///     Value::record(record! {
88///         "first-name" => Value::string("John", span),
89///         "last-name" => Value::string("Doe", span),
90///         "age" => Value::int(42, span),
91///     }, span)
92/// );
93/// ```
94pub trait IntoValue: Sized {
95    /// Converts the given value to a [`Value`].
96    fn into_value(self, span: Span) -> Value;
97}
98
99// Primitive Types
100
101impl<T, const N: usize> IntoValue for [T; N]
102where
103    T: IntoValue,
104{
105    fn into_value(self, span: Span) -> Value {
106        Vec::from(self).into_value(span)
107    }
108}
109
110macro_rules! primitive_into_value {
111    ($type:ty, $method:ident) => {
112        primitive_into_value!($type => $type, $method);
113    };
114
115    ($type:ty => $as_type:ty, $method:ident) => {
116        impl IntoValue for $type {
117            fn into_value(self, span: Span) -> Value {
118                Value::$method(<$as_type>::from(self), span)
119            }
120        }
121    };
122}
123
124primitive_into_value!(bool, bool);
125primitive_into_value!(char, string);
126primitive_into_value!(f32 => f64, float);
127primitive_into_value!(f64, float);
128primitive_into_value!(i8 => i64, int);
129primitive_into_value!(i16 => i64, int);
130primitive_into_value!(i32 => i64, int);
131primitive_into_value!(i64, int);
132primitive_into_value!(u8 => i64, int);
133primitive_into_value!(u16 => i64, int);
134primitive_into_value!(u32 => i64, int);
135// u64 and usize may be truncated as Value only supports i64.
136
137impl IntoValue for isize {
138    fn into_value(self, span: Span) -> Value {
139        Value::int(self as i64, span)
140    }
141}
142
143impl IntoValue for () {
144    fn into_value(self, span: Span) -> Value {
145        Value::nothing(span)
146    }
147}
148
149macro_rules! tuple_into_value {
150    ($($t:ident:$n:tt),+) => {
151        impl<$($t),+> IntoValue for ($($t,)+) where $($t: IntoValue,)+ {
152            fn into_value(self, span: Span) -> Value {
153                let vals = vec![$(self.$n.into_value(span)),+];
154                Value::list(vals, span)
155            }
156        }
157    }
158}
159
160// Tuples in std are implemented for up to 12 elements, so we do it here too.
161tuple_into_value!(T0:0);
162tuple_into_value!(T0:0, T1:1);
163tuple_into_value!(T0:0, T1:1, T2:2);
164tuple_into_value!(T0:0, T1:1, T2:2, T3:3);
165tuple_into_value!(T0:0, T1:1, T2:2, T3:3, T4:4);
166tuple_into_value!(T0:0, T1:1, T2:2, T3:3, T4:4, T5:5);
167tuple_into_value!(T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6);
168tuple_into_value!(T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7);
169tuple_into_value!(T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7, T8:8);
170tuple_into_value!(T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7, T8:8, T9:9);
171tuple_into_value!(T0:0, T1:1, T2:2, T3:3, T4:4, T5:5, T6:6, T7:7, T8:8, T9:9, T10:10);
172tuple_into_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);
173
174// Other std Types
175
176impl IntoValue for String {
177    fn into_value(self, span: Span) -> Value {
178        Value::string(self, span)
179    }
180}
181
182impl IntoValue for &str {
183    fn into_value(self, span: Span) -> Value {
184        Value::string(self, span)
185    }
186}
187
188impl<T> IntoValue for Vec<T>
189where
190    T: IntoValue,
191{
192    fn into_value(self, span: Span) -> Value {
193        Value::list(self.into_iter().map(|v| v.into_value(span)).collect(), span)
194    }
195}
196
197impl<T> IntoValue for Box<T>
198where
199    T: IntoValue,
200{
201    fn into_value(self, span: Span) -> Value {
202        let t: T = *self;
203        t.into_value(span)
204    }
205}
206
207impl<T> IntoValue for Option<T>
208where
209    T: IntoValue,
210{
211    fn into_value(self, span: Span) -> Value {
212        match self {
213            Some(v) => v.into_value(span),
214            None => Value::nothing(span),
215        }
216    }
217}
218
219/// This blanket implementation permits the use of [`Cow<'_, B>`] ([`Cow<'_, str>`] etc) based on
220/// the [IntoValue] implementation of `B`'s owned form ([str] => [String]).
221///
222/// It's meant to make using the [IntoValue] derive macro on types that contain [Cow] fields
223/// possible.
224impl<B> IntoValue for Cow<'_, B>
225where
226    B: ?Sized + ToOwned,
227    B::Owned: IntoValue,
228{
229    fn into_value(self, span: Span) -> Value {
230        <B::Owned as IntoValue>::into_value(self.into_owned(), span)
231    }
232}
233
234impl<K, V> IntoValue for HashMap<K, V>
235where
236    K: Borrow<str> + Into<String>,
237    V: IntoValue,
238{
239    fn into_value(self, span: Span) -> Value {
240        // The `Borrow<str>` constraint is to ensure uniqueness, as implementations of `Borrow`
241        // must uphold by certain properties (e.g., `(x == y) == (x.borrow() == y.borrow())`.
242        //
243        // The `Into<String>` constraint is necessary for us to convert the key into a `String`.
244        // Most types that implement `Borrow<str>` also implement `Into<String>`.
245        // Implementations of `Into` must also be lossless and value-preserving conversions.
246        // So, when combined with the `Borrow` constraint, this means that the converted
247        // `String` keys should be unique.
248        self.into_iter()
249            .map(|(k, v)| (k.into(), v.into_value(span)))
250            .collect::<Record>()
251            .into_value(span)
252    }
253}
254
255impl IntoValue for std::time::Duration {
256    fn into_value(self, span: Span) -> Value {
257        let val: u128 = self.as_nanos();
258        debug_assert!(val <= i64::MAX as u128, "duration value too large");
259        // Capping is the best effort here.
260        let val: i64 = val.try_into().unwrap_or(i64::MAX);
261        Value::duration(val, span)
262    }
263}
264
265impl IntoValue for &std::path::Path {
266    fn into_value(self, span: Span) -> Value {
267        Value::string(self.to_string_lossy(), span)
268    }
269}
270
271impl IntoValue for std::path::PathBuf {
272    fn into_value(self, span: Span) -> Value {
273        Value::string(self.to_string_lossy(), span)
274    }
275}
276
277// Nu Types
278
279impl IntoValue for Range {
280    fn into_value(self, span: Span) -> Value {
281        Value::range(self, span)
282    }
283}
284
285impl IntoValue for Record {
286    fn into_value(self, span: Span) -> Value {
287        Value::record(self, span)
288    }
289}
290
291impl IntoValue for Closure {
292    fn into_value(self, span: Span) -> Value {
293        Value::closure(self, span)
294    }
295}
296
297impl IntoValue for ShellError {
298    fn into_value(self, span: Span) -> Value {
299        Value::error(self, span)
300    }
301}
302
303impl IntoValue for CellPath {
304    fn into_value(self, span: Span) -> Value {
305        Value::cell_path(self, span)
306    }
307}
308
309impl IntoValue for Value {
310    fn into_value(self, span: Span) -> Value {
311        self.with_span(span)
312    }
313}
314
315impl<Form: PathForm> IntoValue for &nu_path::Path<Form> {
316    fn into_value(self, span: Span) -> Value {
317        Value::string(self.to_string_lossy(), span)
318    }
319}
320
321impl<Form: PathForm> IntoValue for nu_path::PathBuf<Form> {
322    fn into_value(self, span: Span) -> Value {
323        Value::string(self.to_string_lossy(), span)
324    }
325}
326
327// Foreign Types
328
329impl IntoValue for DateTime<FixedOffset> {
330    fn into_value(self, span: Span) -> Value {
331        Value::date(self, span)
332    }
333}
334
335impl IntoValue for bytes::Bytes {
336    fn into_value(self, span: Span) -> Value {
337        Value::binary(self.to_vec(), span)
338    }
339}
340
341impl IntoValue for serde_json::Value {
342    fn into_value(self, span: Span) -> Value {
343        match self {
344            serde_json::Value::Null => Value::nothing(span),
345            serde_json::Value::Bool(b) => Value::bool(b, span),
346            serde_json::Value::Number(num) => {
347                if let Some(n) = num.as_i64() {
348                    return Value::int(n, span);
349                }
350                Value::float(
351                    num.as_f64()
352                        .expect("should always return some floating point number"),
353                    span,
354                )
355            }
356            serde_json::Value::String(s) => Value::string(s, span),
357            serde_json::Value::Array(values) => Value::list(
358                values.into_iter().map(|v| v.into_value(span)).collect(),
359                span,
360            ),
361            serde_json::Value::Object(map) => Value::record(
362                Record::from_iter(map.into_iter().map(|(k, v)| (k, v.into_value(span)))),
363                span,
364            ),
365        }
366    }
367}
368
369// TODO: use this type for all the `into_value` methods that types implement but return a Result
370/// A trait for trying to convert a value into a `Value`.
371///
372/// Types like streams may fail while collecting the `Value`,
373/// for these types it is useful to implement a fallible variant.
374///
375/// This conversion is fallible, for infallible conversions use [`IntoValue`].
376/// All types that implement `IntoValue` will automatically implement this trait.
377pub trait TryIntoValue: Sized {
378    // TODO: instead of ShellError, maybe we could have a IntoValueError that implements Into<ShellError>
379    /// Tries to convert the given value into a `Value`.
380    fn try_into_value(self, span: Span) -> Result<Value, ShellError>;
381}
382
383impl<T> TryIntoValue for T
384where
385    T: IntoValue,
386{
387    fn try_into_value(self, span: Span) -> Result<Value, ShellError> {
388        Ok(self.into_value(span))
389    }
390}