scale_value/
macros.rs

1// Copyright (C) 2022-2023 Parity Technologies (UK) Ltd. (admin@parity.io)
2// This file is a part of the scale-value crate.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//         http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16/// Construct a `scale_value::Value` using syntax similar to the `serde_jaon::json!` macro.
17///
18/// Construct values representing structs (SCALE "composite" types with named fields):
19///
20/// ```
21/// use scale_value::value;
22///
23/// let struct_value = value!({
24///     name: "foo",
25///     value: 123,
26///     sub_fields: {
27///         foo: true,
28///         bar: false
29///     }
30/// });
31/// ```
32///
33/// Construct values representing tuples (SCALE composite types with unnamed fields):
34///
35/// ```
36/// use scale_value::value;
37///
38/// let tuple_value = value!{
39///     ("foo", 123, (true, false))
40/// };
41/// ```
42///
43/// Construct enum variants by prefixing the variant name to either of the above constructions:
44///
45/// ```
46/// use scale_value::value;
47///
48/// let variant_with_unnamed_fields = value!{
49///     VariantName("foo", 123)
50/// };
51///
52/// let variant_with_named_fields = value!{
53///     VariantName {
54///         name: "foo",
55///         value: 123,
56///         sub_fields: {
57///             foo: true,
58///             bar: false,
59///             other_variant: AnotherVariant(true, 1,2,3)
60///         }
61///     }
62/// };
63/// ```
64///
65/// Values can be nested in each other:
66///
67/// ```
68/// use scale_value::value;
69///
70/// let data_value = value!((1, v1(1, 2), 3));
71/// let val = value!(POST { data: data_value });
72/// ```
73///
74/// Trailing commas are optional.
75#[macro_export(local_inner_macros)]
76macro_rules! value {
77    ($($tt:tt)*) => {
78        value_internal!($($tt)*)
79    };
80}
81
82/// All patterns can be grouped into 4 main patterns:
83///
84/// ### `value_internal!(@unnamed [ELEMENTS] (REST))`
85///
86/// checks `REST` for certain patterns and if a suitable element pattern is found,
87/// the element is added to the comma seperated `ELEMENTS` list. When `REST` is empty,
88/// we collect all `ELEMENTS` into a Vec of values.
89///
90/// ### `value_internal!(@named [KEYVALUEPAIRS] (PARTIALKEY) (REST))`
91///
92/// goes over the REST tokens, to build up the PARTIALKEY until it is a proper KEY.
93/// This happens as soon as a colon `:` token is encountered, then we switch to the next pattern:
94///
95/// ### `value_internal!(@named [KEYVALUEPAIRS] [KEY] (REST))`
96///
97/// The square brackets now indicate that the key is fully formed. Now REST is scanned for the next
98/// `VALUE`. Together with the `KEY`, they are added as a key value pair tuple into the KEYVALUEPAIRS list.
99/// At that point we switch back to the partial key pattern above, e.g. `value_internal!(@named [KEYVALUEPAIRS] () (REST))`
100/// now with a new empty partial key that has to be filled.
101/// When `REST` is empty, we collect all `KEYVALUEPAIRS` into a Vec of key-value tuples.
102///
103/// ### `value_internal!(BASEPATTERN)`
104///
105/// These patterns check if the input represents a composite or variant type or can be made into a valid `$crate::Value`.
106#[macro_export(local_inner_macros)]
107#[doc(hidden)]
108macro_rules! value_internal {
109
110    ////////////////////////////////////////////////////////////////////////////
111    // collecting unnamed fields
112    ////////////////////////////////////////////////////////////////////////////
113
114    // done, put all the fields in a vec
115    (@unnamed [$($e:expr, )*] ()) => { vec_wrapper![$($e, )*] };
116
117    // Next value is an unnamed composite
118    (@unnamed [$($e:expr, )*] (($($array:tt)*) $($rest:tt)*)) => {
119        value_internal!(@unnamed [$($e, )*] (value_internal!(($($array)*))) $($rest)*)
120    };
121
122    // Next value is an unnamed variant
123    (@unnamed [$($e:expr, )*] ($variant:ident ($($array:tt)*) $($rest:tt)*)) => {
124        value_internal!(@unnamed [$($e, )*] (value_internal!($variant ($($array)*))) $($rest)*)
125    };
126
127    // Next value is a named composite
128    (@unnamed [$($e:expr, )*] ({$($map:tt)*} $($rest:tt)*)) => {
129        value_internal!(@unnamed [$($e, )*] (value_internal!({$($map)*})) $($rest)*)
130    };
131
132    // Next value is a named variant
133    (@unnamed [$($e:expr, )*] ($variant:ident {$($map:tt)*} $($rest:tt)*)) => {
134        value_internal!(@unnamed [$($e, )*] (value_internal!($variant {$($map)*})) $($rest)*)
135    };
136
137    // Insert the current entry followed by trailing comma
138    (@unnamed [$($e:expr, )*] ($value:expr) , $($rest:tt)*) => {
139        value_internal!(@unnamed [$($e, )* $value , ] ($($rest)*))
140    };
141
142    // Current entry followed by unexpected token.
143    // There needs to be a comma, which would match the previous matcher or no further tokens at all matching the next matcher
144     (@unnamed [$($e:expr, )*] ($value:expr) $unexpected:tt $($rest:tt)*) => {
145        let token = core::stringify!($unexpected);
146        compile_error!("unexpected token after expression: {}", token);
147    };
148
149    // Insert the last entry without trailing comma
150    (@unnamed [$($e:expr, )*] ($value:expr)) => {
151        vec_wrapper![ $($e, )* value_internal!($value) ]
152    };
153
154    // Next value is an expression followed by comma
155    (@unnamed [$($e:expr, )*] ($value:expr , $($rest:tt)*)) => {
156        value_internal!(@unnamed [$($e, )*] (value_internal!($value)) , $($rest)*)
157    };
158
159    ////////////////////////////////////////////////////////////////////////////
160    // collecting named fields
161    ////////////////////////////////////////////////////////////////////////////
162
163    // done, put all the key value pairs in a vec
164    (@named [$(($k:expr, $v:expr), )*] () ()) => { vec_wrapper![ $(($k, $v), )* ] };
165
166    // Insert the current entry followed by trailing comma.
167    (@named [$(($k:expr, $v:expr), )*] [$key:expr] ($value:expr) , $($rest:tt)*) => {
168        {
169            let field_name = literal_aware_stringify!($key);
170            value_internal!(@named [$(($k, $v), )* (field_name, $value), ] () ($($rest)*))
171        }
172    };
173
174    // Current entry followed by unexpected token.
175    // There needs to be a comma, which would match the previous matcher or no further tokens at all matching the next matcher
176    (@named [$(($k:expr, $v:expr), )*] [$key:expr] ($value:expr) $unexpected:tt $($rest:tt)*) => {
177        let token = core::stringify!($unexpected);
178        compile_error!("unexpected token after expression: {}", token);
179    };
180
181    // Insert the last entry without trailing comma.
182    (@named [$(($k:expr, $v:expr), )*] [$key:expr] ($value:expr)) => {
183        {
184            let field_name = literal_aware_stringify!($key);
185            vec_wrapper![ $(($k, $v), )* (field_name, $value) ]
186        }
187    };
188
189    // Next value is an unnamed composite
190    (@named [$(($k:expr, $v:expr), )*] ($key:expr) (: ($($array:tt)*) $($rest:tt)*)) => {
191        value_internal!(@named [$(($k, $v), )*] [$key] (value_internal!(($($array)*))) $($rest)*)
192    };
193
194    // Next value is an unnamed variant
195    (@named [$(($k:expr, $v:expr), )*] ($key:expr) (: $variant:ident ($($array:tt)*) $($rest:tt)*)) => {
196        value_internal!(@named [$(($k, $v), )*] [$key] (value_internal!($variant ($($array)*))) $($rest)*)
197    };
198
199    // Next value is a named composite
200    (@named [$(($k:expr, $v:expr), )*] ($key:expr) (: {$($map:tt)*} $($rest:tt)*)) => {
201        value_internal!(@named [$(($k, $v), )*] [$key] (value_internal!({$($map)*})) $($rest)*)
202    };
203
204    // Next value is a named variant
205    (@named [$(($k:expr, $v:expr), )*] ($key:expr) (: $variant:ident {$($map:tt)*} $($rest:tt)*)) => {
206        value_internal!(@named [$(($k, $v), )*] [$key] (value_internal!($variant {$($map)*})) $($rest)*)
207    };
208
209    // // Next value is an expression followed by comma
210    (@named [$(($k:expr, $v:expr), )*] ($key:expr) (: $value:expr , $($rest:tt)*)) => {
211        value_internal!(@named [$(($k, $v), )*] [$key] (value_internal!($value)) , $($rest)*)
212    };
213
214    // Last value is an expression with no trailing comma
215    (@named [$(($k:expr, $v:expr), )*] ($key:expr) (: $value:expr)) => {
216        value_internal!(@named [$(($k, $v), )*] [$key] (value_internal!($value)))
217    };
218
219    // Error pattern: Missing value for last entry
220    (@named [$(($k:expr, $v:expr), )*] ($key:expr) (:)) => {
221        compile_error!("missing value for last entry");
222    };
223
224    // Error pattern: Missing colon and value for last entry
225    (@named [$(($k:expr, $v:expr), )*] ($key:expr) ()) => {
226        compile_error!("missing colon and value for last entry");
227    };
228
229    // Error pattern: colon as first token
230    (@named [$(($k:expr, $v:expr), )*] () (: $($rest:tt)*)) => {
231        compile_error!("colon in wrong position");
232    };
233
234    // Error pattern: comma inside key
235    (@named [$(($k:expr, $v:expr), )*] ($($key:tt)*) (, $($rest:tt)*)) => {
236        compile_error!("comma in key of named composite");
237    };
238
239    (@named [$(($k:expr, $v:expr), )*] () (($key:expr) : $($rest:tt)*)) => {
240        value_internal!(@named [$(($k, $v), )*] ($key) (: $($rest)*))
241    };
242
243    // add a token into the current key.
244    (@named [$(($k:expr, $v:expr), )*] ($($key:tt)*) ($tt:tt $($rest:tt)*)) => {
245        value_internal!(@named [$(($k, $v), )*] ($($key)* $tt) ($($rest)*))
246    };
247
248    ////////////////////////////////////////////////////////////////////////////
249    // Main implementation of patterns:
250    ////////////////////////////////////////////////////////////////////////////
251
252    // empty composites:
253    () => {
254        $crate::Value::unnamed_composite(Vec::<$crate::Value>::new())
255    };
256    (()) => {
257        $crate::Value::unnamed_composite(Vec::<$crate::Value>::new())
258    };
259    ({}) => {
260        $crate::Value::named_composite(Vec::<(String, $crate::Value)>::new())
261    };
262
263    // named composites e.g. { age: 1, nice: false }
264    ({ $($tt:tt)* }) => {
265        {
266            let fields: Vec::<(String, $crate::Value)> = value_internal!(@named [] () ($($tt)*));
267            $crate::Value::named_composite(fields)
268        }
269    };
270
271    // named variants e.g. v1 { age: 1, nice: false }
272    ($variant:ident { $($tt:tt)* }) => {
273        {
274            let variant_name = literal_aware_stringify!($variant);
275            let fields: Vec::<(String, $crate::Value)> = value_internal!(@named [] () ($($tt)*));
276            $crate::Value::named_variant(variant_name,fields)
277        }
278    };
279
280    // unnamed composites with (..) syntax e.g. (1,"hello",3)
281    (( $($tt:tt)* )) => {
282        {
283            let fields = value_internal!(@unnamed [] ($($tt)*));
284            $crate::Value::unnamed_composite(fields)
285        }
286    };
287
288    // unnamed variants e.g. v1 (1,2,3,4)
289    ($variant:ident ( $($tt:tt)* )) => {
290        {
291            let variant_name = literal_aware_stringify!($variant);
292            let fields = value_internal!(@unnamed [] ($($tt)*));
293            $crate::Value::unnamed_variant(variant_name,fields)
294        }
295    };
296
297    // any other expressions
298    ($val:expr) => {
299        $crate::Value::from($val)
300    };
301}
302
303// The value_internal macro above cannot invoke vec directly because it uses
304// local_inner_macros. A vec invocation there would resolve to $crate::vec.
305#[macro_export]
306#[doc(hidden)]
307macro_rules! vec_wrapper {
308    ($($content:tt)*) => {
309        vec![$($content)*]
310    };
311}
312
313/// Just using core::stringify!($key).to_string() on a $key that is a string literal
314/// causes doubled quotes to appear. This macro fixes that.
315#[macro_export]
316#[doc(hidden)]
317macro_rules! literal_aware_stringify {
318    ($tt:literal) => {
319        $tt.to_string()
320    };
321    ($($tt:tt)*) => {
322        stringify!($($tt)*).to_string()
323    };
324}
325
326#[cfg(test)]
327#[macro_use]
328mod test {
329    use crate::prelude::*;
330    use crate::{value, Value};
331
332    #[test]
333    fn macro_tests() {
334        // primitives:
335        assert_eq!(value!(1), Value::from(1));
336        assert_eq!(value!(-122193223i64), Value::from(-122193223i64));
337        assert_eq!(value!(89usize), Value::from(89usize));
338        assert_eq!(value!(false), Value::from(false));
339        assert_eq!(value!(true), Value::from(true));
340        assert_eq!(value!('h'), Value::from('h'));
341        assert_eq!(value!("Hello"), Value::from("Hello"));
342        assert_eq!(value!("Hello".to_string()), Value::from("Hello"));
343        let s = "Hello".to_string();
344        assert_eq!(value!(s), Value::from("Hello"));
345
346        // unnamed composites:
347        let unnamed_composite =
348            Value::unnamed_composite([Value::from(1), Value::from("nice"), Value::from('t')]);
349
350        assert_eq!(value!((1, "nice", 't')), unnamed_composite);
351        assert_eq!(value!((1, "nice", 't',)), unnamed_composite);
352
353        let empty_composite = Value::unnamed_composite([]);
354        assert_eq!(value!(()), empty_composite);
355
356        // named composites:
357        let named_composite =
358            Value::named_composite([("num", Value::from(3)), ("item", Value::from("tea"))]);
359        assert_eq!(value!({num: 3, item: "tea"}), named_composite);
360        assert_eq!(value!({num: 3, item: "tea", }), named_composite);
361        // variants:
362        let variant_no_fields = Value::variant("v1", crate::Composite::Unnamed(vec![]));
363        assert_eq!(value!(v1()), variant_no_fields);
364        let named_variant = Value::variant(
365            "V2",
366            crate::Composite::named([("num", Value::from(3)), ("item", Value::from("tea"))]),
367        );
368        assert_eq!(value!(V2 { num: 3, item: "tea" }), named_variant);
369        // string literal as key:
370        let value = value!({ "string key": 123 });
371        assert_eq!(value, Value::named_composite([("string key", Value::from(123))]));
372        // wild combination, just check if compiles:
373        let _ = value!({ unnamed: unnamed_composite, vals: (v1{name: "berry", age: 34}, named_variant), named: named_composite });
374    }
375}