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 Option<T>
197where
198    T: IntoValue,
199{
200    fn into_value(self, span: Span) -> Value {
201        match self {
202            Some(v) => v.into_value(span),
203            None => Value::nothing(span),
204        }
205    }
206}
207
208/// This blanket implementation permits the use of [`Cow<'_, B>`] ([`Cow<'_, str>`] etc) based on
209/// the [IntoValue] implementation of `B`'s owned form ([str] => [String]).
210///
211/// It's meant to make using the [IntoValue] derive macro on types that contain [Cow] fields
212/// possible.
213impl<B> IntoValue for Cow<'_, B>
214where
215    B: ?Sized + ToOwned,
216    B::Owned: IntoValue,
217{
218    fn into_value(self, span: Span) -> Value {
219        <B::Owned as IntoValue>::into_value(self.into_owned(), span)
220    }
221}
222
223impl<K, V> IntoValue for HashMap<K, V>
224where
225    K: Borrow<str> + Into<String>,
226    V: IntoValue,
227{
228    fn into_value(self, span: Span) -> Value {
229        // The `Borrow<str>` constraint is to ensure uniqueness, as implementations of `Borrow`
230        // must uphold by certain properties (e.g., `(x == y) == (x.borrow() == y.borrow())`.
231        //
232        // The `Into<String>` constraint is necessary for us to convert the key into a `String`.
233        // Most types that implement `Borrow<str>` also implement `Into<String>`.
234        // Implementations of `Into` must also be lossless and value-preserving conversions.
235        // So, when combined with the `Borrow` constraint, this means that the converted
236        // `String` keys should be unique.
237        self.into_iter()
238            .map(|(k, v)| (k.into(), v.into_value(span)))
239            .collect::<Record>()
240            .into_value(span)
241    }
242}
243
244impl IntoValue for std::time::Duration {
245    fn into_value(self, span: Span) -> Value {
246        let val: u128 = self.as_nanos();
247        debug_assert!(val <= i64::MAX as u128, "duration value too large");
248        // Capping is the best effort here.
249        let val: i64 = val.try_into().unwrap_or(i64::MAX);
250        Value::duration(val, span)
251    }
252}
253
254// Nu Types
255
256impl IntoValue for Range {
257    fn into_value(self, span: Span) -> Value {
258        Value::range(self, span)
259    }
260}
261
262impl IntoValue for Record {
263    fn into_value(self, span: Span) -> Value {
264        Value::record(self, span)
265    }
266}
267
268impl IntoValue for Closure {
269    fn into_value(self, span: Span) -> Value {
270        Value::closure(self, span)
271    }
272}
273
274impl IntoValue for ShellError {
275    fn into_value(self, span: Span) -> Value {
276        Value::error(self, span)
277    }
278}
279
280impl IntoValue for CellPath {
281    fn into_value(self, span: Span) -> Value {
282        Value::cell_path(self, span)
283    }
284}
285
286impl IntoValue for Value {
287    fn into_value(self, span: Span) -> Value {
288        self.with_span(span)
289    }
290}
291
292// Foreign Types
293
294impl IntoValue for DateTime<FixedOffset> {
295    fn into_value(self, span: Span) -> Value {
296        Value::date(self, span)
297    }
298}
299
300impl IntoValue for bytes::Bytes {
301    fn into_value(self, span: Span) -> Value {
302        Value::binary(self.to_vec(), span)
303    }
304}
305
306// TODO: use this type for all the `into_value` methods that types implement but return a Result
307/// A trait for trying to convert a value into a `Value`.
308///
309/// Types like streams may fail while collecting the `Value`,
310/// for these types it is useful to implement a fallible variant.
311///
312/// This conversion is fallible, for infallible conversions use [`IntoValue`].
313/// All types that implement `IntoValue` will automatically implement this trait.
314pub trait TryIntoValue: Sized {
315    // TODO: instead of ShellError, maybe we could have a IntoValueError that implements Into<ShellError>
316    /// Tries to convert the given value into a `Value`.
317    fn try_into_value(self, span: Span) -> Result<Value, ShellError>;
318}
319
320impl<T> TryIntoValue for T
321where
322    T: IntoValue,
323{
324    fn try_into_value(self, span: Span) -> Result<Value, ShellError> {
325        Ok(self.into_value(span))
326    }
327}