nu_protocol/value/
into_value.rs

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