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}