crystal_server/
lib.rs

1mod buffer;
2pub mod client;
3mod leb;
4pub mod types;
5
6/// This macro provides a natural way to make a [types::Value] while making it readable
7/// Small sized structures such as [i32] will be converted to their bigger supported structure sizes, [i64].
8/// For integers the type used is [i64]
9/// For floats, [f64]
10/// To declare a [types::Value::Buffer] use a byte-string, such as: ```rust
11/// b"content goes here!"
12/// ```
13/// Here's an example on how you could use this macro:
14/// ```rust
15/// let my_data = value!(["sample text", {
16///     "is_edible": false,
17///     "optional_data": null,
18///     "position": [1.0, 15.0],
19///     "id": 3,
20/// }, b"scary binary data!"]);
21/// ```
22#[macro_export]
23macro_rules! value {
24    // Taken from serde_json's `json_internal` macro but using Crystal Server's data types instead.
25
26    //////////////////////////////////////////////////////////////////////////
27    // TT muncher for parsing the inside of an array [...]. Produces a vec![...]
28    // of the elements.
29    //
30    // Must be invoked as: value!(@array [] $($tt)*)
31    //////////////////////////////////////////////////////////////////////////
32
33    // Done with trailing comma.
34    (@array [$($elems:expr,)*]) => {
35        std::vec![$($elems,)*]
36    };
37
38    // Done without trailing comma.
39    (@array [$($elems:expr),*]) => {
40        std::vec![$($elems),*]
41    };
42
43    // Next element is `null`.
44    (@array [$($elems:expr,)*] null $($rest:tt)*) => {
45        $crate::value!(@array [$($elems,)* $crate::value!(null)] $($rest)*)
46    };
47
48    // Next element is `true`.
49    (@array [$($elems:expr,)*] true $($rest:tt)*) => {
50        $crate::value!(@array [$($elems,)* $crate::value!(true)] $($rest)*)
51    };
52
53    // Next element is `false`.
54    (@array [$($elems:expr,)*] false $($rest:tt)*) => {
55        $crate::value!(@array [$($elems,)* $crate::value!(false)] $($rest)*)
56    };
57
58    // Next element is an array.
59    (@array [$($elems:expr,)*] [$($array:tt)*] $($rest:tt)*) => {
60        $crate::value!(@array [$($elems,)* $crate::value!([$($array)*])] $($rest)*)
61    };
62
63    // Next element is a map.
64    (@array [$($elems:expr,)*] {$($map:tt)*} $($rest:tt)*) => {
65        $crate::value!(@array [$($elems,)* $crate::value!({$($map)*})] $($rest)*)
66    };
67
68    // Next element is an expression followed by comma.
69    (@array [$($elems:expr,)*] $next:expr, $($rest:tt)*) => {
70        $crate::value!(@array [$($elems,)* $crate::value!($next),] $($rest)*)
71    };
72
73    // Last element is an expression with no trailing comma.
74    (@array [$($elems:expr,)*] $last:expr) => {
75        $crate::value!(@array [$($elems,)* $crate::value!($last)])
76    };
77
78    // Comma after the most recent element.
79    (@array [$($elems:expr),*] , $($rest:tt)*) => {
80        $crate::value!(@array [$($elems,)*] $($rest)*)
81    };
82
83    // Unexpected token after most recent element.
84    (@array [$($elems:expr),*] $unexpected:tt $($rest:tt)*) => {
85        $crate::value_unexpected!($unexpected)
86    };
87
88    //////////////////////////////////////////////////////////////////////////
89    // TT muncher for parsing the inside of an object {...}. Each entry is
90    // inserted into the given map variable.
91    //
92    // Must be invoked as: json_internal!(@object $map () ($($tt)*) ($($tt)*))
93    //
94    // We require two copies of the input tokens so that we can match on one
95    // copy and trigger errors on the other copy.
96    //////////////////////////////////////////////////////////////////////////
97
98    // Done.
99    (@object $object:ident () () ()) => {};
100
101    // Insert the current entry followed by trailing comma.
102    (@object $object:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => {
103        let _ = $object.insert(($($key)+).into(), $value);
104        $crate::value!(@object $object () ($($rest)*) ($($rest)*));
105    };
106
107    // Current entry followed by unexpected token.
108    (@object $object: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    (@object $object:ident [$($key:tt)+] ($value:expr)) => {
114        let _ = $object.insert(($($key)+).into(), $value);
115    };
116
117    // Next value is `null`.
118    (@object $object:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => {
119        $crate::value!(@object $object [$($key)+] ($crate::value!(null)) $($rest)*);
120    };
121
122    // Next value is `true`.
123    (@object $object:ident ($($key:tt)+) (: true $($rest:tt)*) $copy:tt) => {
124        $crate::value!(@object $object [$($key)+] ($crate::value!(true)) $($rest)*);
125    };
126
127    // Next value is `false`.
128    (@object $object:ident ($($key:tt)+) (: false $($rest:tt)*) $copy:tt) => {
129        $crate::value!(@object $object [$($key)+] ($crate::value!(false)) $($rest)*);
130    };
131
132    // Next value is an array.
133    (@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => {
134        $crate::value!(@object $object [$($key)+] ($crate::value!([$($array)*])) $($rest)*);
135    };
136
137    // Next value is a map.
138    (@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => {
139        $crate::value!(@object $object [$($key)+] ($crate::value!({$($map)*})) $($rest)*);
140    };
141
142    // Next value is an expression followed by comma.
143    (@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => {
144        $crate::value!(@object $object [$($key)+] ($crate::value!($value)) , $($rest)*);
145    };
146
147    // Last value is an expression with no trailing comma.
148    (@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => {
149        $crate::value!(@object $object [$($key)+] ($crate::value!($value)));
150    };
151
152    // Missing value for last entry. Trigger a reasonable error message.
153    (@object $object:ident ($($key:tt)+) (:) $copy:tt) => {
154        // "unexpected end of macro invocation"
155        $crate::value!();
156    };
157
158    // Missing colon and value for last entry. Trigger a reasonable error
159    // message.
160    (@object $object:ident ($($key:tt)+) () $copy:tt) => {
161        // "unexpected end of macro invocation"
162        $crate::value!();
163    };
164
165    // Misplaced colon. Trigger a reasonable error message.
166    (@object $object: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    (@object $object: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    (@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => {
180        $crate::value!(@object $object ($key) (: $($rest)*) (: $($rest)*));
181    };
182
183    // Refuse to absorb colon token into key expression.
184    (@object $object:ident ($($key:tt)*) (: $($unexpected:tt)+) $copy:tt) => {
185        $crate::value_expect_expr_comma!($($unexpected)+);
186    };
187
188    // Munch a token into the current key.
189    (@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => {
190        $crate::value!(@object $object ($($key)* $tt) ($($rest)*) ($($rest)*));
191    };
192
193    //////////////////////////////////////////////////////////////////////////
194    // The main implementation.
195    //
196    // Must be invoked as: value!($($json)+)
197    //////////////////////////////////////////////////////////////////////////
198
199    (null) => {
200        $crate::types::Value::Null
201    };
202
203    (true) => {
204        $crate::types::Value::Bool(true)
205    };
206
207    (false) => {
208        $crate::types::Value::Bool(false)
209    };
210
211    ([]) => {
212        $crate::types::Value::Array(std::vec![])
213    };
214
215    ([ $($tt:tt)+ ]) => {
216        $crate::types::Value::Array($crate::value!(@array [] $($tt)+))
217    };
218
219    ({}) => {
220        $crate::types::Value::Struct(std::collections::HashMap::new())
221    };
222
223    ({ $($tt:tt)+ }) => {
224        $crate::types::Value::Struct({
225            let mut object = std::collections::HashMap::new();
226            $crate::value!(@object object () ($($tt)+) ($($tt)+));
227            object
228        })
229    };
230
231    // Any Serialize type: numbers, strings, struct literals, variables etc.
232    // Must be below every other rule.
233    ($other:expr) => {
234        $crate::types::Value::from(&$other)
235    };
236}
237
238#[macro_export]
239#[doc(hidden)]
240macro_rules! value_unexpected {
241    () => {};
242}
243
244#[macro_export]
245#[doc(hidden)]
246macro_rules! value_expect_expr_comma {
247    ($e:expr , $($tt:tt)*) => {};
248}