Skip to main content

ijson/
macros.rs

1macro_rules! value_subtype_impls {
2    ($t:ty, $cf:ident, $rcf:ident, $mcf:ident) => {
3        impl std::convert::AsRef<crate::IValue> for $t {
4            fn as_ref(&self) -> &crate::IValue {
5                &self.0
6            }
7        }
8        impl std::convert::AsMut<crate::IValue> for $t {
9            fn as_mut(&mut self) -> &mut crate::IValue {
10                &mut self.0
11            }
12        }
13        impl std::borrow::Borrow<crate::IValue> for $t {
14            fn borrow(&self) -> &crate::IValue {
15                &self.0
16            }
17        }
18        impl std::borrow::BorrowMut<crate::IValue> for $t {
19            fn borrow_mut(&mut self) -> &mut crate::IValue {
20                &mut self.0
21            }
22        }
23        impl std::convert::From<$t> for crate::IValue {
24            fn from(other: $t) -> Self {
25                other.0
26            }
27        }
28        impl std::convert::TryFrom<crate::IValue> for $t {
29            type Error = crate::IValue;
30            fn try_from(other: crate::IValue) -> Result<Self, crate::IValue> {
31                other.$cf()
32            }
33        }
34        impl<'a> std::convert::TryFrom<&'a crate::IValue> for &'a $t {
35            type Error = ();
36            fn try_from(other: &'a crate::IValue) -> Result<Self, ()> {
37                other.$rcf().ok_or(())
38            }
39        }
40        impl<'a> std::convert::TryFrom<&'a mut crate::IValue> for &'a mut $t {
41            type Error = ();
42            fn try_from(other: &'a mut crate::IValue) -> Result<Self, ()> {
43                other.$mcf().ok_or(())
44            }
45        }
46    };
47}
48
49macro_rules! typed_conversions {
50    ($(
51        $interm:ty: $(
52            $src:ty
53            $(where ($($gb:tt)*))*
54        ),*;
55    )*) => {
56        $(
57            $(
58                impl $(<$($gb)*>)* From<$src> for IValue {
59                    fn from(other: $src) -> Self {
60                        <$interm>::from(other).into()
61                    }
62                }
63            )*
64        )*
65    }
66}
67
68/// Construct an [`IValue`] using familiar JSON syntax.
69///
70/// For example:
71/// ```
72/// use ijson::{ijson, IValue};
73///
74/// let _: IValue = ijson!({ "foo": null, "bar": [1, 2, 3, 4] });
75/// ```
76///
77/// [`IValue`]: super::IValue
78#[macro_export(local_inner_macros)]
79macro_rules! ijson {
80    // Hide implementation details from the generated rustdoc.
81    ($($json:tt)+) => {
82        $crate::ijson_internal!($($json)+)
83    };
84}
85
86#[macro_export(local_inner_macros)]
87#[doc(hidden)]
88macro_rules! ijson_internal {
89    // Done without trailing comma.
90    (@array $array:ident) => {};
91
92    // Done with trailing comma.
93    (@array $array:ident ,) => {};
94
95    // Next element is `null`.
96    (@array $array:ident , null $($rest:tt)*) => {
97        $array.push(ijson_internal!(null));
98        ijson_internal!(@array $array $($rest)*)
99    };
100
101    // Next element is `true`.
102    (@array $array:ident , true $($rest:tt)*) => {
103        $array.push(ijson_internal!(true));
104        ijson_internal!(@array $array $($rest)*)
105    };
106
107    // Next element is `false`.
108    (@array $array:ident , false $($rest:tt)*) => {
109        $array.push(ijson_internal!(false));
110        ijson_internal!(@array $array $($rest)*)
111    };
112
113    // Next element is an array.
114    (@array $array:ident , [$($arr:tt)*] $($rest:tt)*) => {
115        $array.push(ijson_internal!([$($arr)*]));
116        ijson_internal!(@array $array $($rest)*)
117    };
118
119    // Next element is an object.
120    (@array $array:ident , {$($obj:tt)*} $($rest:tt)*) => {
121        $array.push(ijson_internal!({$($obj)*}));
122        ijson_internal!(@array $array $($rest)*)
123    };
124
125    // Next element is an expression followed by comma.
126    (@array $array:ident , $next:expr , $($rest:tt)*) => {
127        $array.push(ijson_internal!($next));
128        ijson_internal!(@array $array , $($rest)*)
129    };
130
131    // Last element is an expression with no trailing comma.
132    (@array $array:ident , $last:expr) => {
133        $array.push(ijson_internal!($last));
134    };
135
136    // Unexpected token after most recent element.
137    (@array $array:ident , $unexpected:tt $($rest:tt)*) => {
138        ijson_unexpected!($unexpected)
139    };
140
141    // Unexpected token after most recent element.
142    (@array $array:ident $unexpected:tt $($rest:tt)*) => {
143        ijson_unexpected!($unexpected)
144    };
145
146    // Done.
147    (@object $object:ident () () ()) => {};
148
149    // Insert the current entry followed by trailing comma.
150    (@object $object:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => {
151        let _ = $object.insert(($($key)+), $value);
152        ijson_internal!(@object $object () ($($rest)*) ($($rest)*));
153    };
154
155    // Current entry followed by unexpected token.
156    (@object $object:ident [$($key:tt)+] ($value:expr) $unexpected:tt $($rest:tt)*) => {
157        ijson_unexpected!($unexpected);
158    };
159
160    // Insert the last entry without trailing comma.
161    (@object $object:ident [$($key:tt)+] ($value:expr)) => {
162        let _ = $object.insert(($($key)+), $value);
163    };
164
165    // Next value is `null`.
166    (@object $object:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => {
167        ijson_internal!(@object $object [$($key)+] (ijson_internal!(null)) $($rest)*);
168    };
169
170    // Next value is `true`.
171    (@object $object:ident ($($key:tt)+) (: true $($rest:tt)*) $copy:tt) => {
172        ijson_internal!(@object $object [$($key)+] (ijson_internal!(true)) $($rest)*);
173    };
174
175    // Next value is `false`.
176    (@object $object:ident ($($key:tt)+) (: false $($rest:tt)*) $copy:tt) => {
177        ijson_internal!(@object $object [$($key)+] (ijson_internal!(false)) $($rest)*);
178    };
179
180    // Next value is an array.
181    (@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => {
182        ijson_internal!(@object $object [$($key)+] (ijson_internal!([$($array)*])) $($rest)*);
183    };
184
185    // Next value is a map.
186    (@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => {
187        ijson_internal!(@object $object [$($key)+] (ijson_internal!({$($map)*})) $($rest)*);
188    };
189
190    // Next value is an expression followed by comma.
191    (@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => {
192        ijson_internal!(@object $object [$($key)+] (ijson_internal!($value)) , $($rest)*);
193    };
194
195    // Last value is an expression with no trailing comma.
196    (@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => {
197        ijson_internal!(@object $object [$($key)+] (ijson_internal!($value)));
198    };
199
200    // Missing value for last entry. Trigger a reasonable error message.
201    (@object $object:ident ($($key:tt)+) (:) $copy:tt) => {
202        // "unexpected end of macro invocation"
203        ijson_internal!();
204    };
205
206    // Missing colon and value for last entry. Trigger a reasonable error
207    // message.
208    (@object $object:ident ($($key:tt)+) () $copy:tt) => {
209        // "unexpected end of macro invocation"
210        ijson_internal!();
211    };
212
213    // Misplaced colon. Trigger a reasonable error message.
214    (@object $object:ident () (: $($rest:tt)*) ($colon:tt $($copy:tt)*)) => {
215        // Takes no arguments so "no rules expected the token `:`".
216        ijson_unexpected!($colon);
217    };
218
219    // Found a comma inside a key. Trigger a reasonable error message.
220    (@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => {
221        // Takes no arguments so "no rules expected the token `,`".
222        ijson_unexpected!($comma);
223    };
224
225    // Key is fully parenthesized. This avoids clippy double_parens false
226    // positives because the parenthesization may be necessary here.
227    (@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => {
228        ijson_internal!(@object $object ($key) (: $($rest)*) (: $($rest)*));
229    };
230
231    // Refuse to absorb colon token into key expression.
232    (@object $object:ident ($($key:tt)*) (: $($unexpected:tt)+) $copy:tt) => {
233        ijson_expect_expr_comma!($($unexpected)+);
234    };
235
236    // Munch a token into the current key.
237    (@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => {
238        ijson_internal!(@object $object ($($key)* $tt) ($($rest)*) ($($rest)*));
239    };
240
241    //////////////////////////////////////////////////////////////////////////
242    // The main implementation.
243    //
244    // Must be invoked as: ijson_internal!($($json)+)
245    //////////////////////////////////////////////////////////////////////////
246
247    (null) => {
248        $crate::IValue::NULL
249    };
250
251    (true) => {
252        $crate::IValue::TRUE
253    };
254
255    (false) => {
256        $crate::IValue::FALSE
257    };
258
259    ([]) => {
260        $crate::IValue::from($crate::IArray::new())
261    };
262
263    ([ $($tt:tt)+ ]) => {
264        $crate::IValue::from({
265            let mut array = $crate::IArray::new();
266            ijson_internal!(@array array , $($tt)+);
267            array
268        })
269    };
270
271    ({}) => {
272        $crate::IValue::from($crate::IObject::new())
273    };
274
275    ({ $($tt:tt)+ }) => {
276        $crate::IValue::from({
277            let mut object = $crate::IObject::new();
278            ijson_internal!(@object object () ($($tt)+) ($($tt)+));
279            object
280        })
281    };
282
283    // Any Serialize type: numbers, strings, struct literals, variables etc.
284    // Must be below every other rule.
285    ($other:expr) => {
286        $crate::to_value(&$other).unwrap()
287    };
288}
289
290#[macro_export]
291#[doc(hidden)]
292macro_rules! ijson_unexpected {
293    () => {};
294}
295
296#[macro_export]
297#[doc(hidden)]
298macro_rules! ijson_expect_expr_comma {
299    ($e:expr , $($tt:tt)*) => {};
300}