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}