Skip to main content

nu_protocol/value/
macros.rs

1#[macro_export]
2macro_rules! record {
3    {$($col:expr => $val:expr),* $(,)?} => {
4        $crate::Record::from_iter(::std::vec![ $(
5            (::std::string::String::from($col), $val)
6        ),* ])
7    };
8}
9
10/// Helper for constructing [Value::Record] instances for use in tests and
11/// [Example](crate::Example)s
12/// ```
13/// # use nu_protocol::{Value, test_record, record};
14/// let test = test_record! {
15///     "a" => "foo",
16///     "b" => 42,
17///     "c" => [1, 2, 3],
18/// };
19///
20/// let expected = Value::test_record(record! {
21///     "a" => Value::test_string("foo"),
22///     "b" => Value::test_int(42),
23///     "c" => Value::test_list(vec![
24///         Value::test_int(1),
25///         Value::test_int(2),
26///         Value::test_int(3),
27///     ]),
28/// });
29///
30/// assert_eq!(test, expected);
31/// ```
32#[macro_export]
33macro_rules! test_record {
34    {$($col:expr => $val:expr),* $(,)?} => {
35        $crate::Value::test_record($crate::record! { $(
36            $col => $crate::IntoValue::into_value($val, $crate::Span::test_data())
37        ),* })
38    };
39}
40
41#[doc(hidden)]
42pub const fn count_helper<const N: usize>(_: [(); N]) -> usize {
43    N
44}
45
46/// Helper for constructing table (list of records) values for use in tests and
47/// [Example](crate::Example)s
48/// ```
49/// # use nu_protocol::{Value, test_table, test_record, record};
50/// let test = test_table![
51///     ["a", "b", "c"];
52///     [1, 2, 3],
53///     [4, 5, 6],
54/// ];
55///
56/// let expected = Value::test_list(vec![
57///     test_record! {"a" => 1, "b" => 2, "c" => 3},
58///     test_record! {"a" => 4, "b" => 5, "c" => 6},
59/// ]);
60///
61/// assert_eq!(test, expected);
62/// ```
63#[macro_export]
64macro_rules! test_table {
65    (@replace_expr $_t:tt $sub:expr) => { $sub };
66    (@count_tts $($smth:tt)*) => {
67        $crate::macros::count_helper([$($crate::test_table!(@replace_expr $smth ())),*])
68    };
69    [[$($col:expr),+ $(,)?]; $([$($val:expr),+ $(,)?]),+ $(,)?] => {{
70        const COLUMNS: usize = $crate::test_table!(@count_tts $($col)+);
71        let columns: ::std::vec::Vec<::std::string::String> = ::std::vec![$($col.into()),+];
72        let rows = vec![ $(
73            {
74                const ROW_ITEMS: usize = $crate::test_table!(@count_tts $($val)+);
75                const _: () = assert!(ROW_ITEMS == COLUMNS) ;
76                $crate::Value::test_record($crate::Record::from_raw_cols_vals(
77                    columns.clone(),
78                    ::std::vec![ $(
79                        $crate::IntoValue::into_value($val, $crate::Span::test_data())
80                    ),+ ],
81                    $crate::Span::test_data(),
82                    $crate::Span::test_data(),
83                ).expect("Number of columns and rows should be equal"))
84            }
85        ),+ ];
86        $crate::Value::test_list(rows)
87    }};
88}
89
90/// Helper macro for constructing [`Value::List`] instances for use in tests and
91/// [Examples](crate::Example)s.
92///
93/// ```
94/// # use nu_protocol::*;
95/// #
96/// let test = test_list![
97///     "abc",
98///     42,
99///     true,
100/// ];
101///
102/// let expected = Value::test_list(vec![
103///     Value::test_string("abc"),
104///     Value::test_int(42),
105///     Value::test_bool(true),
106/// ]);
107///
108/// assert_eq!(test, expected);
109/// ```
110#[macro_export]
111macro_rules! test_list {
112    [$($entry:expr),* $(,)?] => {
113        $crate::Value::test_list(::std::vec![
114            $($crate::IntoValue::into_value($entry, $crate::Span::test_data())),*
115        ])
116    };
117}
118
119/// Helper macro for constructing [`Value`] instances for use in tests and
120/// [Examples](crate::Example)s.
121///
122/// Can be used to create simple scalar values with anything implementing
123/// [IntoValue](crate::IntoValue):
124/// ```
125/// # use nu_protocol::*;
126/// assert_eq!(test_value!(42),   Value::test_int(42));
127/// assert_eq!(test_value!(true), Value::test_bool(true));
128/// assert_eq!(test_value!(()),   Value::test_nothing());
129/// ```
130///
131/// Can be used in place of [`test_list!`]:
132/// ```
133/// # use nu_protocol::*;
134/// let test =   test_value!(["abc", 42, true]);
135/// let expected = test_list!["abc", 42, true];
136/// assert_eq!(test, expected);
137/// ```
138///
139/// Can be used in place of [`test_record!`], with some differences:
140/// - instead of fat arrows (`=>`), colons are used (`:`).
141/// - keys can be bare identifiers in addition to string literals and variables.
142///   (to use the value of an existing variable, wrap it in parentheses)
143/// ```
144/// # use nu_protocol::*;
145/// let key_in_var = "foo";
146/// let test = test_value!({
147///     a: 1,
148///     "b": 2,
149///     (key_in_var): "bar",
150/// });
151/// let expected = test_record! {
152///     "a" => 1,
153///     "b" => 2,
154///     "foo" => "bar",
155/// };
156/// assert_eq!(test, expected);
157/// ```
158///
159/// The most important feature of [`test_value!`] is that it works recursively for all values.
160/// That makes it very powerful for constructing complex and nested values:
161/// ```
162/// # use nu_protocol::*;
163/// let test = test_value!({
164///     a: 1,
165///     b: {
166///         c: 2,
167///         d: ["e", "f", {g: 3}],
168///     },
169/// });
170/// let expected = test_record! {
171///     "a" => 1,
172///     "b" => test_record! {
173///         "c" => 2,
174///         "d" => test_list! ["e", "f", test_record! { "g" => 3 } ],
175///     },
176/// };
177/// assert_eq!(test, expected);
178/// ```
179#[macro_export]
180macro_rules! test_value {
181    (@recur, [$($item:tt),* $(,)?]) => {
182        $crate::test_list![$(
183            $crate::test_value!(@recur, $item)
184        ),*]
185    };
186    (@recur, {$($col:tt : $val:tt),* $(,)?}) => {
187        $crate::test_record! { $(
188            $crate::test_value!(@col, $col) => $crate::test_value!(@recur, $val)
189        ),* }
190    };
191    (@recur, $val:expr) => { $val };
192
193    (@col, $col:ident) => { stringify!($col) };
194    (@col, $col:expr) => { $col };
195
196    // top level calls
197    ([$($item:tt),* $(,)?]) => { $crate::test_value!(@recur, [$($item),*]) };
198    ({$($col:tt : $val:tt),* $(,)?}) => { $crate::test_value!(@recur, {$($col : $val),*}) };
199    ($val:expr) => { $crate::IntoValue::into_value($val, $crate::Span::test_data()) };
200}
201
202#[cfg(test)]
203mod test_value_macro_tests {
204    use pretty_assertions::assert_eq;
205
206    use crate::{IntoValue, Span};
207
208    #[test]
209    fn ident_record_columns() {
210        let foo_col = "foo_val";
211        let x = test_value!({
212            a: 2,
213            b: 3,
214            foo_col: foo_col,
215            (foo_col): foo_col,
216        });
217
218        let expected = test_record! {
219            "a" => 2,
220            "b" => 3,
221            "foo_col" => "foo_val",
222            "foo_val" => "foo_val",
223        };
224
225        assert_eq!(x, expected);
226    }
227
228    #[test]
229    fn simple_values() {
230        let x = test_value!(10);
231        let expected = 10.into_value(Span::test_data());
232
233        assert_eq!(x, expected);
234
235        let x = test_value!(true);
236        let expected = true.into_value(Span::test_data());
237
238        assert_eq!(x, expected);
239
240        let x = test_value!(());
241        let expected = ().into_value(Span::test_data());
242
243        assert_eq!(x, expected);
244    }
245
246    #[test]
247    fn simple_record() {
248        let x = test_value!({
249            "a": 1,
250            "b": 2,
251            "c": 3,
252        });
253
254        let expected = test_record! {
255            "a" => 1,
256            "b" => 2,
257            "c" => 3,
258        };
259
260        assert_eq!(x, expected);
261    }
262
263    #[test]
264    fn simple_list() {
265        let x = test_value!(["abc", 42, true,]);
266
267        let expected = test_list!["abc", 42, true,];
268
269        assert_eq!(x, expected);
270    }
271
272    #[test]
273    fn nested_records() {
274        let x = test_value!({
275            "a": 1,
276            "b": 2,
277            "c": {
278                "d": 4,
279                "e": 5,
280                "f": {
281                    "g": 7,
282                    "h": 8,
283                }
284            },
285        });
286
287        let expected = test_record! {
288            "a" => 1,
289            "b" => 2,
290            "c" => test_record! {
291                "d" => 4,
292                "e" => 5,
293                "f" => test_record! {
294                    "g" => 7,
295                    "h" => 8,
296                }
297            },
298        };
299
300        assert_eq!(x, expected);
301    }
302
303    #[test]
304    fn nested_lists() {
305        let x = test_value!(["a", "b", ["c", "d", ["e", "f",],],]);
306
307        let expected = test_list!["a", "b", test_list!["c", "d", test_list!["e", "f",],],];
308
309        assert_eq!(x, expected);
310    }
311
312    #[test]
313    fn complex_value() {
314        let x = test_value!({
315            "a": 1,
316            "b": {
317                "b_a": 3,
318                "b_b": 4,
319            },
320            "c": [1, "two", ()],
321            "d": [
322                {"foo": 1, "bar": 10},
323                {"foo": 2, "bar": 20},
324                {"foo": 3, "bar": 30},
325            ],
326        });
327
328        let expected = test_record! {
329            "a" => 1,
330            "b" => test_record! {
331                "b_a" => 3,
332                "b_b" => 4,
333            },
334            "c" => test_list![1, "two", ()],
335            "d" => test_list![
336                test_record! {"foo" => 1, "bar" => 10},
337                test_record! {"foo" => 2, "bar" => 20},
338                test_record! {"foo" => 3, "bar" => 30},
339            ],
340        };
341
342        assert_eq!(x, expected)
343    }
344
345    #[test]
346    fn complex_value_with_ident_keys() {
347        let x = test_value!({
348            a: 1,
349            b: {
350                b_a: 3,
351                b_b: 4,
352            },
353            c: [1, "two", ()],
354            d: [
355                {foo: 1, bar: 10},
356                {foo: 2, bar: 20},
357                {foo: 3, bar: 30},
358            ],
359        });
360
361        let expected = test_record! {
362            "a" => 1,
363            "b" => test_record! {
364                "b_a" => 3,
365                "b_b" => 4,
366            },
367            "c" => test_list![1, "two", ()],
368            "d" => test_list![
369                test_record! {"foo" => 1, "bar" => 10},
370                test_record! {"foo" => 2, "bar" => 20},
371                test_record! {"foo" => 3, "bar" => 30},
372            ],
373        };
374
375        assert_eq!(x, expected)
376    }
377}