Skip to main content

deser_value/
macros.rs

1/// Constructs a [`Value`](crate::Value) from a JSON like literal.
2///
3/// ```
4/// use deser_value::value;
5///
6/// let id = 42;
7/// let value = value!({
8///     "id": id,
9///     "name": "Jane",
10///     "tags": ["admin", null, 1.5],
11///     "nested": {"deep": true},
12///     // keys can be any value
13///     1: "one",
14///     (id + 1): "expression",
15/// });
16/// assert_eq!(value["nested"]["deep"], true);
17/// assert_eq!(value[&value!(43)], "expression");
18/// ```
19///
20/// Values and keys can be any expression that converts into a value, keys
21/// can also be sequences and maps.  Keys which are expressions made of more
22/// than one token (other than a leading minus) have to be put into
23/// parentheses.
24#[macro_export]
25macro_rules! value {
26    ($($value:tt)+) => {
27        $crate::__value_internal!($($value)+)
28    };
29}
30
31// This follows the implementation of `serde_json::json!`.
32#[macro_export]
33#[doc(hidden)]
34macro_rules! __value_internal {
35    // Sequences: `(@seq [$($elems,)*] $($rest)*)` munches the elements
36    // into `$elems`.
37
38    // Done with trailing comma.
39    (@seq [$($elems:expr,)*]) => {
40        ::std::vec![$($elems,)*]
41    };
42
43    // Done without trailing comma.
44    (@seq [$($elems:expr),*]) => {
45        ::std::vec![$($elems),*]
46    };
47
48    // Next element is `null`.
49    (@seq [$($elems:expr,)*] null $($rest:tt)*) => {
50        $crate::__value_internal!(@seq [$($elems,)* $crate::__value_internal!(null)] $($rest)*)
51    };
52
53    // Next element is `true`.
54    (@seq [$($elems:expr,)*] true $($rest:tt)*) => {
55        $crate::__value_internal!(@seq [$($elems,)* $crate::__value_internal!(true)] $($rest)*)
56    };
57
58    // Next element is `false`.
59    (@seq [$($elems:expr,)*] false $($rest:tt)*) => {
60        $crate::__value_internal!(@seq [$($elems,)* $crate::__value_internal!(false)] $($rest)*)
61    };
62
63    // Next element is a sequence.
64    (@seq [$($elems:expr,)*] [$($seq:tt)*] $($rest:tt)*) => {
65        $crate::__value_internal!(@seq [$($elems,)* $crate::__value_internal!([$($seq)*])] $($rest)*)
66    };
67
68    // Next element is a map.
69    (@seq [$($elems:expr,)*] {$($map:tt)*} $($rest:tt)*) => {
70        $crate::__value_internal!(@seq [$($elems,)* $crate::__value_internal!({$($map)*})] $($rest)*)
71    };
72
73    // Next element is an expression followed by comma.
74    (@seq [$($elems:expr,)*] $next:expr, $($rest:tt)*) => {
75        $crate::__value_internal!(@seq [$($elems,)* $crate::__value_internal!($next),] $($rest)*)
76    };
77
78    // Last element is an expression with no trailing comma.
79    (@seq [$($elems:expr,)*] $last:expr) => {
80        $crate::__value_internal!(@seq [$($elems,)* $crate::__value_internal!($last)])
81    };
82
83    // Comma after the most recent element.
84    (@seq [$($elems:expr),*] , $($rest:tt)*) => {
85        $crate::__value_internal!(@seq [$($elems,)*] $($rest)*)
86    };
87
88    // Unexpected token after most recent element.
89    (@seq [$($elems:expr),*] $unexpected:tt $($rest:tt)*) => {
90        $crate::__value_unexpected!($unexpected)
91    };
92
93    // Maps: `(@map $map ($($key)*) ($($rest)*) ($($rest)*))` munches the
94    // tokens of the next key into `$key` and inserts the entries into the
95    // map.  The rest is passed twice so that the first token of it can be
96    // used in error messages.
97
98    // Done.
99    (@map $map:ident () () ()) => {};
100
101    // Insert the current entry followed by trailing comma.
102    (@map $map:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => {
103        let _ = $map.insert(($($key)+), $value);
104        $crate::__value_internal!(@map $map () ($($rest)*) ($($rest)*));
105    };
106
107    // Current entry followed by unexpected token.
108    (@map $map:ident [$($key:tt)+] ($value:expr) $unexpected:tt $($rest:tt)*) => {
109        $crate::__value_unexpected!($unexpected);
110    };
111
112    // Insert the last entry without trailing comma.
113    (@map $map:ident [$($key:tt)+] ($value:expr)) => {
114        let _ = $map.insert(($($key)+), $value);
115    };
116
117    // Next value is `null`.
118    (@map $map:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => {
119        $crate::__value_internal!(@map $map [$($key)+] ($crate::__value_internal!(null)) $($rest)*);
120    };
121
122    // Next value is `true`.
123    (@map $map:ident ($($key:tt)+) (: true $($rest:tt)*) $copy:tt) => {
124        $crate::__value_internal!(@map $map [$($key)+] ($crate::__value_internal!(true)) $($rest)*);
125    };
126
127    // Next value is `false`.
128    (@map $map:ident ($($key:tt)+) (: false $($rest:tt)*) $copy:tt) => {
129        $crate::__value_internal!(@map $map [$($key)+] ($crate::__value_internal!(false)) $($rest)*);
130    };
131
132    // Next value is a sequence.
133    (@map $map:ident ($($key:tt)+) (: [$($seq:tt)*] $($rest:tt)*) $copy:tt) => {
134        $crate::__value_internal!(@map $map [$($key)+] ($crate::__value_internal!([$($seq)*])) $($rest)*);
135    };
136
137    // Next value is a map.
138    (@map $map:ident ($($key:tt)+) (: {$($inner:tt)*} $($rest:tt)*) $copy:tt) => {
139        $crate::__value_internal!(@map $map [$($key)+] ($crate::__value_internal!({$($inner)*})) $($rest)*);
140    };
141
142    // Next value is an expression followed by comma.
143    (@map $map:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => {
144        $crate::__value_internal!(@map $map [$($key)+] ($crate::__value_internal!($value)) , $($rest)*);
145    };
146
147    // Last value is an expression with no trailing comma.
148    (@map $map:ident ($($key:tt)+) (: $value:expr) $copy:tt) => {
149        $crate::__value_internal!(@map $map [$($key)+] ($crate::__value_internal!($value)));
150    };
151
152    // Missing value for last entry.  Trigger a reasonable error message.
153    (@map $map:ident ($($key:tt)+) (:) $copy:tt) => {
154        // "unexpected end of macro invocation"
155        $crate::__value_internal!();
156    };
157
158    // Missing colon and value for last entry.  Trigger a reasonable error
159    // message.
160    (@map $map:ident ($($key:tt)+) () $copy:tt) => {
161        // "unexpected end of macro invocation"
162        $crate::__value_internal!();
163    };
164
165    // Misplaced colon.  Trigger a reasonable error message.
166    (@map $map:ident () (: $($rest:tt)*) ($colon:tt $($copy:tt)*)) => {
167        // Takes no arguments so "no rules expected the token `:`".
168        $crate::__value_unexpected!($colon);
169    };
170
171    // Found a comma inside a key.  Trigger a reasonable error message.
172    (@map $map:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => {
173        // Takes no arguments so "no rules expected the token `,`".
174        $crate::__value_unexpected!($comma);
175    };
176
177    // Key is fully parenthesized.  This avoids clippy double_parens false
178    // positives because the parenthesization may be necessary here.
179    (@map $map:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => {
180        $crate::__value_internal!(@map $map ($key) (: $($rest)*) (: $($rest)*));
181    };
182
183    // Key is a sequence.
184    (@map $map:ident () ([$($key:tt)*] : $($rest:tt)*) $copy:tt) => {
185        $crate::__value_internal!(@map $map ($crate::__value_internal!([$($key)*])) (: $($rest)*) (: $($rest)*));
186    };
187
188    // Key is a map.
189    (@map $map:ident () ({$($key:tt)*} : $($rest:tt)*) $copy:tt) => {
190        $crate::__value_internal!(@map $map ($crate::__value_internal!({$($key)*})) (: $($rest)*) (: $($rest)*));
191    };
192
193    // Key is `null`.
194    (@map $map:ident () (null : $($rest:tt)*) $copy:tt) => {
195        $crate::__value_internal!(@map $map ($crate::__value_internal!(null)) (: $($rest)*) (: $($rest)*));
196    };
197
198    // Refuse to absorb colon token into key expression.
199    (@map $map:ident ($($key:tt)*) (: $($unexpected:tt)+) $copy:tt) => {
200        $crate::__value_expect_expr_comma!($($unexpected)+);
201    };
202
203    // Munch a token into the current key.
204    (@map $map:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => {
205        $crate::__value_internal!(@map $map ($($key)* $tt) ($($rest)*) ($($rest)*));
206    };
207
208    // The main implementation.
209
210    (null) => {
211        $crate::Value::null()
212    };
213
214    (true) => {
215        $crate::Value::from(true)
216    };
217
218    (false) => {
219        $crate::Value::from(false)
220    };
221
222    ([]) => {
223        $crate::Value::from($crate::Seq::new())
224    };
225
226    ([ $($tt:tt)+ ]) => {
227        $crate::Value::from($crate::__value_internal!(@seq [] $($tt)+))
228    };
229
230    ({}) => {
231        $crate::Value::from($crate::Map::new())
232    };
233
234    ({ $($tt:tt)+ }) => {
235        $crate::Value::from({
236            let mut map = $crate::Map::new();
237            $crate::__value_internal!(@map map () ($($tt)+) ($($tt)+));
238            map
239        })
240    };
241
242    // Any value that converts into a value.  Must be below every other rule.
243    ($other:expr) => {
244        $crate::Value::from($other)
245    };
246}
247
248#[macro_export]
249#[doc(hidden)]
250macro_rules! __value_unexpected {
251    () => {};
252}
253
254#[macro_export]
255#[doc(hidden)]
256macro_rules! __value_expect_expr_comma {
257    ($e:expr , $($tt:tt)*) => {};
258}