nu_protocol/value/
into_value.rs

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