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}