/src/OpenSK/libraries/cbor/src/macros.rs
Line | Count | Source |
1 | | // Copyright 2019 Google LLC |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | //! Convenience macros for working with CBOR values. |
16 | | |
17 | | use crate::values::Value; |
18 | | use alloc::vec; |
19 | | use core::cmp::Ordering; |
20 | | use core::iter::Peekable; |
21 | | |
22 | | /// This macro generates code to extract multiple values from a `Vec<(Value, Value)>` at once |
23 | | /// in an optimized manner, consuming the input vector. |
24 | | /// |
25 | | /// It takes as input a `Vec` as well as a list of identifiers and keys, and generates code |
26 | | /// that assigns the corresponding values to new variables using the given identifiers. Each of |
27 | | /// these variables has type `Option<Value>`, to account for the case where keys aren't found. |
28 | | /// |
29 | | /// **Important:** Keys passed to the `destructure_cbor_map!` macro **must be sorted** in increasing |
30 | | /// order. If not, the algorithm can yield incorrect results, such a assigning `None` to a variable |
31 | | /// even if the corresponding key existed in the map. **No runtime checks** are made for this in the |
32 | | /// `destructure_cbor_map!` macro, in order to avoid overhead at runtime. However, assertions that |
33 | | /// keys are sorted are added in `cfg(test)` mode, so that unit tests can verify ahead of time that |
34 | | /// the keys are indeed sorted. This macro is therefore **not suitable for dynamic keys** that can |
35 | | /// change at runtime. |
36 | | /// |
37 | | /// Example usage: |
38 | | /// |
39 | | /// ```rust |
40 | | /// # extern crate alloc; |
41 | | /// # use sk_cbor::destructure_cbor_map; |
42 | | /// # |
43 | | /// # fn main() { |
44 | | /// # let map = alloc::vec::Vec::new(); |
45 | | /// destructure_cbor_map! { |
46 | | /// let { |
47 | | /// 1 => x, |
48 | | /// "key" => y, |
49 | | /// } = map; |
50 | | /// } |
51 | | /// # } |
52 | | /// ``` |
53 | | #[macro_export] |
54 | | macro_rules! destructure_cbor_map { |
55 | | ( let { $( $key:expr => $variable:ident, )+ } = $map:expr; ) => { |
56 | | // A pre-requisite for this algorithm to work is that the keys to extract from the map are |
57 | | // sorted - the behavior is unspecified if the keys are not sorted. |
58 | | // Therefore, in test mode we add assertions that the keys are indeed sorted. |
59 | | #[cfg(test)] |
60 | | $crate::assert_sorted_keys!($( $key, )+); |
61 | | |
62 | | use $crate::values::{IntoCborValue, Value}; |
63 | | use $crate::macros::destructure_cbor_map_peek_value; |
64 | | |
65 | | // This algorithm first converts the map into a peekable iterator - whose items are sorted |
66 | | // in strictly increasing order of keys. Then, the repeated calls to the "peek value" |
67 | | // helper function will consume this iterator and yield values (or `None`) when reaching |
68 | | // the keys to extract. |
69 | | // |
70 | | // This is where the pre-requisite that keys to extract are sorted is important: the |
71 | | // algorithm does a single linear scan over the iterator and therefore keys to extract have |
72 | | // to come in the same order (i.e. sorted). |
73 | | let mut it = $map.into_iter().peekable(); |
74 | | $( |
75 | | let $variable: Option<Value> = destructure_cbor_map_peek_value(&mut it, $key.into_cbor_value()); |
76 | | )+ |
77 | | }; |
78 | | } |
79 | | |
80 | | /// This function is an internal detail of the `destructure_cbor_map!` macro, but has public |
81 | | /// visibility so that users of the macro can use it. |
82 | | /// |
83 | | /// Given a peekable iterator of key-value pairs sorted in strictly increasing key order and a |
84 | | /// needle key, this function consumes all items whose key compares less than or equal to the |
85 | | /// needle, and returns `Some(value)` if the needle was present as the key in the iterator and |
86 | | /// `None` otherwise. |
87 | | /// |
88 | | /// The logic is separated into its own function to reduce binary size, as otherwise the logic |
89 | | /// would be inlined for every use case. As of June 2020, this saves ~40KB of binary size for the |
90 | | /// CTAP2 application of OpenSK. |
91 | 146k | pub fn destructure_cbor_map_peek_value( |
92 | 146k | it: &mut Peekable<vec::IntoIter<(Value, Value)>>, |
93 | 146k | needle: Value, |
94 | 146k | ) -> Option<Value> { |
95 | 149k | loop { |
96 | 149k | match it.peek() { |
97 | 53.0k | None => return None, |
98 | 96.1k | Some(item) => { |
99 | 96.1k | let key: &Value = &item.0; |
100 | 96.1k | match key.cmp(&needle) { |
101 | 2.87k | Ordering::Less => { |
102 | 2.87k | it.next(); |
103 | 2.87k | } |
104 | | Ordering::Equal => { |
105 | 52.8k | let value: Value = it.next().unwrap().1; |
106 | 52.8k | return Some(value); |
107 | | } |
108 | 40.3k | Ordering::Greater => return None, |
109 | | } |
110 | | } |
111 | | } |
112 | | } |
113 | 146k | } |
114 | | |
115 | | /// Assert that the keys in a vector of key-value pairs are in canonical order. |
116 | | #[macro_export] |
117 | | macro_rules! assert_sorted_keys { |
118 | | // Last key |
119 | | ( $key:expr, ) => { |
120 | | }; |
121 | | |
122 | | ( $key1:expr, $key2:expr, $( $keys:expr, )* ) => { |
123 | | { |
124 | | use $crate::values::{IntoCborValue, Value}; |
125 | | let k1: Value = $key1.into_cbor_value(); |
126 | | let k2: Value = $key2.into_cbor_value(); |
127 | | assert!( |
128 | | k1 < k2, |
129 | | "{:?} < {:?} failed. The destructure_cbor_map! macro requires keys in sorted order.", |
130 | | k1, |
131 | | k2, |
132 | | ); |
133 | | } |
134 | | $crate::assert_sorted_keys!($key2, $( $keys, )*); |
135 | | }; |
136 | | } |
137 | | |
138 | | /// Creates a CBOR Value of type Map with the specified key-value pairs. |
139 | | /// |
140 | | /// Keys and values are expressions and converted into CBOR Keys and Values. |
141 | | /// The syntax for these pairs is `key_expression => value_expression,`. |
142 | | /// Keys do not have to be sorted. |
143 | | /// |
144 | | /// # Panics |
145 | | /// |
146 | | /// You may not call this function with identical keys in its argument. |
147 | | /// |
148 | | /// Example usage: |
149 | | /// |
150 | | /// ```rust |
151 | | /// # extern crate alloc; |
152 | | /// # use sk_cbor::cbor_map; |
153 | | /// let map = cbor_map! { |
154 | | /// 0x01 => false, |
155 | | /// "02" => -3, |
156 | | /// }; |
157 | | /// ``` |
158 | | #[macro_export] |
159 | | macro_rules! cbor_map { |
160 | | // trailing comma case |
161 | | ( $( $key:expr => $value:expr, )+ ) => { |
162 | | cbor_map! ( $($key => $value),+ ) |
163 | | }; |
164 | | |
165 | | ( $( $key:expr => $value:expr ),* ) => { |
166 | | { |
167 | | // The import is unused if the list is empty. |
168 | | #[allow(unused_imports)] |
169 | | use $crate::values::IntoCborValue; |
170 | | let mut _map = ::alloc::vec::Vec::new(); |
171 | | $( |
172 | | _map.push(($key.into_cbor_value(), $value.into_cbor_value())); |
173 | | )* |
174 | | $crate::values::Value::map(_map) |
175 | | } |
176 | | }; |
177 | | } |
178 | | |
179 | | /// Creates a CBOR Value of type Map with key-value pairs where values can be Options. |
180 | | /// |
181 | | /// Keys and values are expressions and converted into CBOR Keys and Value Options. |
182 | | /// The map entry is included iff the Value is not an Option or Option is Some. |
183 | | /// The syntax for these pairs is `key_expression => value_expression,`. |
184 | | /// Duplicate keys will lead to invalid CBOR, i.e. writing these values fails. |
185 | | /// Keys do not have to be sorted. |
186 | | /// |
187 | | /// Example usage: |
188 | | /// |
189 | | /// ```rust |
190 | | /// # extern crate alloc; |
191 | | /// # use sk_cbor::cbor_map_options; |
192 | | /// let missing_value: Option<bool> = None; |
193 | | /// let map = cbor_map_options! { |
194 | | /// 0x01 => Some(false), |
195 | | /// "02" => -3, |
196 | | /// "not in map" => missing_value, |
197 | | /// }; |
198 | | /// ``` |
199 | | #[macro_export] |
200 | | macro_rules! cbor_map_options { |
201 | | // trailing comma case |
202 | | ( $( $key:expr => $value:expr, )+ ) => { |
203 | | cbor_map_options! ( $($key => $value),+ ) |
204 | | }; |
205 | | |
206 | | ( $( $key:expr => $value:expr ),* ) => { |
207 | | { |
208 | | // The import is unused if the list is empty. |
209 | | #[allow(unused_imports)] |
210 | | use $crate::values::{IntoCborValue, IntoCborValueOption}; |
211 | | let mut _map = ::alloc::vec::Vec::<(_, $crate::values::Value)>::new(); |
212 | | $( |
213 | | { |
214 | | let opt: Option<$crate::values::Value> = $value.into_cbor_value_option(); |
215 | | if let Some(val) = opt { |
216 | | _map.push(($key.into_cbor_value(), val)); |
217 | | } |
218 | | } |
219 | | )* |
220 | | $crate::values::Value::map(_map) |
221 | | } |
222 | | }; |
223 | | } |
224 | | |
225 | | /// Creates a CBOR Value of type Map from a Vec<(Value, Value)>. |
226 | | #[macro_export] |
227 | | macro_rules! cbor_map_collection { |
228 | | ( $tree:expr ) => {{ |
229 | | $crate::values::Value::map($tree) |
230 | | }}; |
231 | | } |
232 | | |
233 | | /// Creates a CBOR Value of type Array with the given elements. |
234 | | /// |
235 | | /// Elements are expressions and converted into CBOR Values. Elements are comma-separated. |
236 | | /// |
237 | | /// Example usage: |
238 | | /// |
239 | | /// ```rust |
240 | | /// # extern crate alloc; |
241 | | /// # use sk_cbor::cbor_array; |
242 | | /// let array = cbor_array![1, "2"]; |
243 | | /// ``` |
244 | | #[macro_export] |
245 | | macro_rules! cbor_array { |
246 | | // trailing comma case |
247 | | ( $( $value:expr, )+ ) => { |
248 | | cbor_array! ( $($value),+ ) |
249 | | }; |
250 | | |
251 | | ( $( $value:expr ),* ) => { |
252 | | { |
253 | | // The import is unused if the list is empty. |
254 | | #[allow(unused_imports)] |
255 | | use $crate::values::IntoCborValue; |
256 | | $crate::values::Value::array(vec![ $( $value.into_cbor_value(), )* ]) |
257 | | } |
258 | | }; |
259 | | } |
260 | | |
261 | | /// Creates a CBOR Value of type Array from a Vec<Value>. |
262 | | #[macro_export] |
263 | | macro_rules! cbor_array_vec { |
264 | | ( $vec:expr ) => {{ |
265 | | use $crate::values::IntoCborValue; |
266 | | $crate::values::Value::array($vec.into_iter().map(|x| x.into_cbor_value()).collect()) |
267 | | }}; |
268 | | } |
269 | | |
270 | | /// Creates a CBOR Value of type Simple with value true. |
271 | | #[macro_export] |
272 | | macro_rules! cbor_true { |
273 | | ( ) => { |
274 | | $crate::values::Value::bool_value(true) |
275 | | }; |
276 | | } |
277 | | |
278 | | /// Creates a CBOR Value of type Simple with value false. |
279 | | #[macro_export] |
280 | | macro_rules! cbor_false { |
281 | | ( ) => { |
282 | | $crate::values::Value::bool_value(false) |
283 | | }; |
284 | | } |
285 | | |
286 | | /// Creates a CBOR Value of type Simple with value null. |
287 | | #[macro_export] |
288 | | macro_rules! cbor_null { |
289 | | ( ) => { |
290 | | $crate::values::Value::null_value() |
291 | | }; |
292 | | } |
293 | | |
294 | | /// Creates a CBOR Value of type Simple with the undefined value. |
295 | | #[macro_export] |
296 | | macro_rules! cbor_undefined { |
297 | | ( ) => { |
298 | | $crate::values::Value::undefined() |
299 | | }; |
300 | | } |
301 | | |
302 | | /// Creates a CBOR Value of type Simple with the given bool value. |
303 | | #[macro_export] |
304 | | macro_rules! cbor_bool { |
305 | | ( $x:expr ) => { |
306 | | $crate::values::Value::bool_value($x) |
307 | | }; |
308 | | } |
309 | | |
310 | | /// Creates a CBOR Value of type Unsigned with the given numeric value. |
311 | | #[macro_export] |
312 | | macro_rules! cbor_unsigned { |
313 | | ( $x:expr ) => { |
314 | | $crate::values::Value::unsigned($x as u64) |
315 | | }; |
316 | | } |
317 | | |
318 | | /// Creates a CBOR Value of type Unsigned or Negative with the given numeric value. |
319 | | #[macro_export] |
320 | | macro_rules! cbor_int { |
321 | | ( $x:expr ) => { |
322 | | $crate::values::Value::integer($x as i64) |
323 | | }; |
324 | | } |
325 | | |
326 | | /// Creates a CBOR Value of type Text String with the given string. |
327 | | #[macro_export] |
328 | | macro_rules! cbor_text { |
329 | | ( $x:expr ) => { |
330 | | $crate::values::Value::text_string($x.into()) |
331 | | }; |
332 | | } |
333 | | |
334 | | /// Creates a CBOR Value of type Byte String with the given slice or vector. |
335 | | #[macro_export] |
336 | | macro_rules! cbor_bytes { |
337 | | ( $x:expr ) => { |
338 | | $crate::values::Value::byte_string($x) |
339 | | }; |
340 | | } |
341 | | |
342 | | /// Creates a CBOR Value of type Tag with the given tag and object. |
343 | | #[macro_export] |
344 | | macro_rules! cbor_tagged { |
345 | | ( $t:expr, $x: expr ) => { |
346 | | $crate::values::Value::tag($t, $x) |
347 | | }; |
348 | | } |
349 | | |
350 | | /// Creates a CBOR Value of type Byte String with the given byte string literal. |
351 | | /// |
352 | | /// Example usage: |
353 | | /// |
354 | | /// ```rust |
355 | | /// # extern crate alloc; |
356 | | /// # use sk_cbor::cbor_bytes_lit; |
357 | | /// let byte_array = cbor_bytes_lit!(b"foo"); |
358 | | /// ``` |
359 | | #[macro_export] |
360 | | macro_rules! cbor_bytes_lit { |
361 | | ( $x:expr ) => { |
362 | | $crate::cbor_bytes!(($x as &[u8]).to_vec()) |
363 | | }; |
364 | | } |
365 | | |
366 | | #[cfg(test)] |
367 | | mod test { |
368 | | use super::super::values::Value; |
369 | | use alloc::string::String; |
370 | | use alloc::vec; |
371 | | use alloc::vec::Vec; |
372 | | |
373 | | #[test] |
374 | | fn test_cbor_simple_values() { |
375 | | assert_eq!(cbor_true!(), Value::bool_value(true)); |
376 | | assert_eq!(cbor_false!(), Value::bool_value(false)); |
377 | | assert_eq!(cbor_null!(), Value::null_value()); |
378 | | assert_eq!(cbor_undefined!(), Value::undefined()); |
379 | | } |
380 | | |
381 | | #[test] |
382 | | fn test_cbor_bool() { |
383 | | assert_eq!(cbor_bool!(true), Value::bool_value(true)); |
384 | | assert_eq!(cbor_bool!(false), Value::bool_value(false)); |
385 | | } |
386 | | |
387 | | #[test] |
388 | | fn test_cbor_int_unsigned() { |
389 | | assert_eq!(cbor_int!(0), Value::from(0)); |
390 | | assert_eq!(cbor_int!(1), Value::from(1)); |
391 | | assert_eq!(cbor_int!(123456), Value::from(123456)); |
392 | | assert_eq!( |
393 | | cbor_int!(core::i64::MAX), |
394 | | Value::from(core::i64::MAX as u64) |
395 | | ); |
396 | | } |
397 | | |
398 | | #[test] |
399 | | fn test_cbor_int_negative() { |
400 | | assert_eq!(cbor_int!(-1), Value::from(-1)); |
401 | | assert_eq!(cbor_int!(-123456), Value::from(-123456)); |
402 | | assert_eq!(cbor_int!(core::i64::MIN), Value::from(core::i64::MIN)); |
403 | | } |
404 | | |
405 | | #[test] |
406 | | fn test_cbor_int_literals() { |
407 | | let a = cbor_array![ |
408 | | core::i64::MIN, |
409 | | core::i32::MIN, |
410 | | -123456, |
411 | | -1, |
412 | | 0, |
413 | | 1, |
414 | | 123456, |
415 | | core::i32::MAX, |
416 | | core::i64::MAX, |
417 | | core::u64::MAX, |
418 | | ]; |
419 | | let b = Value::array(vec![ |
420 | | Value::from(core::i64::MIN), |
421 | | Value::from(core::i32::MIN as i64), |
422 | | Value::from(-123456), |
423 | | Value::from(-1), |
424 | | Value::from(0), |
425 | | Value::from(1), |
426 | | Value::from(123456), |
427 | | Value::from(core::i32::MAX as u64), |
428 | | Value::from(core::i64::MAX as u64), |
429 | | Value::from(core::u64::MAX), |
430 | | ]); |
431 | | assert_eq!(a, b); |
432 | | } |
433 | | |
434 | | #[test] |
435 | | fn test_cbor_array() { |
436 | | let a = cbor_array![ |
437 | | -123, |
438 | | 456, |
439 | | true, |
440 | | cbor_null!(), |
441 | | "foo", |
442 | | b"bar", |
443 | | cbor_array![], |
444 | | cbor_array![0, 1], |
445 | | cbor_map! {}, |
446 | | cbor_map! {2 => 3}, |
447 | | ]; |
448 | | let b = Value::array(vec![ |
449 | | Value::from(-123), |
450 | | Value::from(456), |
451 | | Value::bool_value(true), |
452 | | Value::null_value(), |
453 | | Value::from(String::from("foo")), |
454 | | Value::from(b"bar".to_vec()), |
455 | | Value::array(Vec::new()), |
456 | | Value::array(vec![Value::from(0), Value::from(1)]), |
457 | | Value::map(Vec::new()), |
458 | | Value::map([(Value::from(2), Value::from(3))].iter().cloned().collect()), |
459 | | ]); |
460 | | assert_eq!(a, b); |
461 | | } |
462 | | |
463 | | #[test] |
464 | | fn test_cbor_array_vec_empty() { |
465 | | let a = cbor_array_vec!(Vec::<bool>::new()); |
466 | | let b = Value::array(Vec::new()); |
467 | | assert_eq!(a, b); |
468 | | } |
469 | | |
470 | | #[test] |
471 | | fn test_cbor_array_vec_int() { |
472 | | let a = cbor_array_vec!(vec![1, 2, 3, 4]); |
473 | | let b = Value::array(vec![ |
474 | | Value::from(1), |
475 | | Value::from(2), |
476 | | Value::from(3), |
477 | | Value::from(4), |
478 | | ]); |
479 | | assert_eq!(a, b); |
480 | | } |
481 | | |
482 | | #[test] |
483 | | fn test_cbor_array_vec_text() { |
484 | | let a = cbor_array_vec!(vec!["a", "b", "c"]); |
485 | | let b = Value::array(vec![ |
486 | | Value::from(String::from("a")), |
487 | | Value::from(String::from("b")), |
488 | | Value::from(String::from("c")), |
489 | | ]); |
490 | | assert_eq!(a, b); |
491 | | } |
492 | | |
493 | | #[test] |
494 | | fn test_cbor_array_vec_bytes() { |
495 | | let a = cbor_array_vec!(vec![b"a", b"b", b"c"]); |
496 | | let b = Value::array(vec![ |
497 | | Value::from(b"a".to_vec()), |
498 | | Value::from(b"b".to_vec()), |
499 | | Value::from(b"c".to_vec()), |
500 | | ]); |
501 | | assert_eq!(a, b); |
502 | | } |
503 | | |
504 | | #[test] |
505 | | fn test_cbor_map() { |
506 | | let a = cbor_map! { |
507 | | 4 => 56, |
508 | | 5 => "foo", |
509 | | 6 => b"bar", |
510 | | 7 => cbor_array![], |
511 | | 8 => cbor_array![0, 1], |
512 | | 9 => cbor_map!{}, |
513 | | 10 => cbor_map!{2 => 3}, |
514 | | -1 => -23, |
515 | | b"bar" => cbor_null!(), |
516 | | "foo" => true, |
517 | | }; |
518 | | let b = Value::map( |
519 | | [ |
520 | | (Value::from(4), Value::from(56)), |
521 | | (Value::from(5), Value::from(String::from("foo"))), |
522 | | (Value::from(6), Value::from(b"bar".to_vec())), |
523 | | (Value::from(7), Value::array(Vec::new())), |
524 | | ( |
525 | | Value::from(8), |
526 | | Value::array(vec![Value::from(0), Value::from(1)]), |
527 | | ), |
528 | | (Value::from(9), Value::map(Vec::new())), |
529 | | ( |
530 | | Value::from(10), |
531 | | Value::map([(Value::from(2), Value::from(3))].iter().cloned().collect()), |
532 | | ), |
533 | | (Value::from(-1), Value::from(-23)), |
534 | | (Value::from(b"bar".to_vec()), Value::null_value()), |
535 | | (Value::from(String::from("foo")), Value::bool_value(true)), |
536 | | ] |
537 | | .iter() |
538 | | .cloned() |
539 | | .collect(), |
540 | | ); |
541 | | assert_eq!(a, b); |
542 | | } |
543 | | |
544 | | #[test] |
545 | | fn test_cbor_map_options() { |
546 | | let a = cbor_map_options! { |
547 | | 4 => Some(56), |
548 | | 5 => "foo", |
549 | | 6 => Some(b"bar" as &[u8]), |
550 | | 7 => cbor_array![], |
551 | | 8 => Some(cbor_array![0, 1]), |
552 | | 9 => cbor_map!{}, |
553 | | 10 => Some(cbor_map!{2 => 3}), |
554 | | 11 => None::<String>, |
555 | | 12 => None::<&str>, |
556 | | 13 => None::<Vec<u8>>, |
557 | | 14 => None::<&[u8]>, |
558 | | 15 => None::<bool>, |
559 | | 16 => None::<i32>, |
560 | | 17 => None::<i64>, |
561 | | 18 => None::<u64>, |
562 | | -1 => -23, |
563 | | b"bar" => Some(cbor_null!()), |
564 | | "foo" => true, |
565 | | }; |
566 | | let b = Value::map( |
567 | | [ |
568 | | (Value::from(4), Value::from(56)), |
569 | | (Value::from(5), Value::from(String::from("foo"))), |
570 | | (Value::from(6), Value::from(b"bar".to_vec())), |
571 | | (Value::from(7), Value::array(Vec::new())), |
572 | | ( |
573 | | Value::from(8), |
574 | | Value::array(vec![Value::from(0), Value::from(1)]), |
575 | | ), |
576 | | (Value::from(9), Value::map(Vec::new())), |
577 | | ( |
578 | | Value::from(10), |
579 | | Value::map([(Value::from(2), Value::from(3))].iter().cloned().collect()), |
580 | | ), |
581 | | (Value::from(-1), Value::from(-23)), |
582 | | (Value::from(b"bar".to_vec()), Value::null_value()), |
583 | | (Value::from(String::from("foo")), Value::bool_value(true)), |
584 | | ] |
585 | | .iter() |
586 | | .cloned() |
587 | | .collect(), |
588 | | ); |
589 | | assert_eq!(a, b); |
590 | | } |
591 | | |
592 | | #[test] |
593 | | fn test_cbor_map_collection_empty() { |
594 | | let a = cbor_map_collection!(Vec::<(_, _)>::new()); |
595 | | let b = Value::map(Vec::new()); |
596 | | assert_eq!(a, b); |
597 | | } |
598 | | |
599 | | #[test] |
600 | | fn test_cbor_map_collection_foo() { |
601 | | let a = cbor_map_collection!(vec![(Value::from(2), Value::from(3))]); |
602 | | let b = Value::map(vec![(Value::from(2), Value::from(3))]); |
603 | | assert_eq!(a, b); |
604 | | } |
605 | | |
606 | | fn extract_map(cbor_value: Value) -> Vec<(Value, Value)> { |
607 | | cbor_value.extract_map().unwrap() |
608 | | } |
609 | | |
610 | | #[test] |
611 | | fn test_destructure_cbor_map_simple() { |
612 | | let map = cbor_map! { |
613 | | 1 => 10, |
614 | | 2 => 20, |
615 | | }; |
616 | | |
617 | | destructure_cbor_map! { |
618 | | let { |
619 | | 1 => x1, |
620 | | 2 => x2, |
621 | | } = extract_map(map); |
622 | | } |
623 | | |
624 | | assert_eq!(x1, Some(cbor_unsigned!(10))); |
625 | | assert_eq!(x2, Some(cbor_unsigned!(20))); |
626 | | } |
627 | | |
628 | | #[test] |
629 | | #[should_panic] |
630 | | fn test_destructure_cbor_map_unsorted() { |
631 | | let map = cbor_map! { |
632 | | 1 => 10, |
633 | | 2 => 20, |
634 | | }; |
635 | | |
636 | | destructure_cbor_map! { |
637 | | // The keys are not sorted here, which violates the precondition of |
638 | | // destructure_cbor_map. An assertion should catch that and make the test panic. |
639 | | let { |
640 | | 2 => _x2, |
641 | | 1 => _x1, |
642 | | } = extract_map(map); |
643 | | } |
644 | | } |
645 | | |
646 | | #[test] |
647 | | fn test_destructure_cbor_map_partial() { |
648 | | let map = cbor_map! { |
649 | | 1 => 10, |
650 | | 2 => 20, |
651 | | 3 => 30, |
652 | | 4 => 40, |
653 | | 5 => 50, |
654 | | 6 => 60, |
655 | | 7 => 70, |
656 | | 8 => 80, |
657 | | 9 => 90, |
658 | | }; |
659 | | |
660 | | destructure_cbor_map! { |
661 | | let { |
662 | | 3 => x3, |
663 | | 7 => x7, |
664 | | } = extract_map(map); |
665 | | } |
666 | | |
667 | | assert_eq!(x3, Some(cbor_unsigned!(30))); |
668 | | assert_eq!(x7, Some(cbor_unsigned!(70))); |
669 | | } |
670 | | |
671 | | #[test] |
672 | | fn test_destructure_cbor_map_missing() { |
673 | | let map = cbor_map! { |
674 | | 1 => 10, |
675 | | 3 => 30, |
676 | | 4 => 40, |
677 | | }; |
678 | | |
679 | | destructure_cbor_map! { |
680 | | let { |
681 | | 0 => x0, |
682 | | 1 => x1, |
683 | | 2 => x2, |
684 | | 3 => x3, |
685 | | 4 => x4, |
686 | | 5 => x5, |
687 | | } = extract_map(map); |
688 | | } |
689 | | |
690 | | assert_eq!(x0, None); |
691 | | assert_eq!(x1, Some(cbor_unsigned!(10))); |
692 | | assert_eq!(x2, None); |
693 | | assert_eq!(x3, Some(cbor_unsigned!(30))); |
694 | | assert_eq!(x4, Some(cbor_unsigned!(40))); |
695 | | assert_eq!(x5, None); |
696 | | } |
697 | | |
698 | | #[test] |
699 | | fn test_destructure_unsorted_cbor_map() { |
700 | | let map = cbor_map! { |
701 | | 2 => 20, |
702 | | 1 => 10, |
703 | | }; |
704 | | |
705 | | destructure_cbor_map! { |
706 | | let { |
707 | | 1 => x1, |
708 | | 2 => x2, |
709 | | } = extract_map(map); |
710 | | } |
711 | | |
712 | | assert_eq!(x1, Some(cbor_unsigned!(10))); |
713 | | assert_eq!(x2, Some(cbor_unsigned!(20))); |
714 | | } |
715 | | } |