1use ciborium::value::Value as CborValue;
2use std::borrow::Borrow;
3use std::convert::TryFrom;
4use std::iter::FromIterator;
5use std::{collections::BTreeMap, convert::TryInto};
6
7use crate::util::cbor_value::value_to_hash;
8use crate::ProtocolError;
9
10pub trait CborBTreeMapHelper {
11 fn get_optional_identifier(&self, key: &str) -> Result<Option<[u8; 32]>, ProtocolError>;
12 fn get_identifier(&self, key: &str) -> Result<[u8; 32], ProtocolError>;
13 fn get_optional_string(&self, key: &str) -> Result<Option<String>, ProtocolError>;
14 fn get_string(&self, key: &str) -> Result<String, ProtocolError>;
15 fn get_optional_str(&self, key: &str) -> Result<Option<&str>, ProtocolError>;
16 fn get_str(&self, key: &str) -> Result<&str, ProtocolError>;
17 fn get_optional_integer<T: TryFrom<i128>>(&self, key: &str)
18 -> Result<Option<T>, ProtocolError>;
19 fn get_integer<T: TryFrom<i128>>(&self, key: &str) -> Result<T, ProtocolError>;
20 fn get_optional_bool(&self, key: &str) -> Result<Option<bool>, ProtocolError>;
21 fn get_bool(&self, key: &str) -> Result<bool, ProtocolError>;
22 fn get_optional_inner_value_array<'a, I: FromIterator<&'a CborValue>>(
23 &'a self,
24 key: &str,
25 ) -> Result<Option<I>, ProtocolError>;
26 fn get_inner_value_array<'a, I: FromIterator<&'a CborValue>>(
27 &'a self,
28 key: &str,
29 ) -> Result<I, ProtocolError>;
30 fn get_optional_inner_string_array<I: FromIterator<String>>(
31 &self,
32 key: &str,
33 ) -> Result<Option<I>, ProtocolError>;
34 fn get_inner_string_array<I: FromIterator<String>>(
35 &self,
36 key: &str,
37 ) -> Result<I, ProtocolError>;
38 fn get_optional_inner_borrowed_str_value_map<'a, I: FromIterator<(String, &'a CborValue)>>(
39 &'a self,
40 key: &str,
41 ) -> Result<Option<I>, ProtocolError>;
42 fn get_optional_inner_borrowed_map(
43 &self,
44 key: &str,
45 ) -> Result<Option<&Vec<(CborValue, CborValue)>>, ProtocolError>;
46 fn get_inner_borrowed_str_value_map<'a, I: FromIterator<(String, &'a CborValue)>>(
47 &'a self,
48 key: &str,
49 ) -> Result<I, ProtocolError>;
50
51 fn remove_optional_integer<T: TryFrom<i128>>(
52 &mut self,
53 key: &str,
54 ) -> Result<Option<T>, ProtocolError>;
55 fn remove_integer<T: TryFrom<i128>>(&mut self, key: &str) -> Result<T, ProtocolError>;
56}
57
58pub trait CborMapExtension {
59 fn as_u16(&self, key: &str, error_message: &str) -> Result<u16, ProtocolError>;
60 fn as_u8(&self, key: &str, error_message: &str) -> Result<u8, ProtocolError>;
61 fn as_bool(&self, key: &str, error_message: &str) -> Result<bool, ProtocolError>;
62 fn as_bytes(&self, key: &str, error_message: &str) -> Result<Vec<u8>, ProtocolError>;
63 fn as_string(&self, key: &str, error_message: &str) -> Result<String, ProtocolError>;
64 fn as_u64(&self, key: &str, error_message: &str) -> Result<u64, ProtocolError>;
65}
66
67impl<V> CborBTreeMapHelper for BTreeMap<String, V>
68where
69 V: Borrow<CborValue>,
70{
71 fn get_optional_identifier(&self, key: &str) -> Result<Option<[u8; 32]>, ProtocolError> {
72 self.get(key).map(|i| value_to_hash(i.borrow())).transpose()
73 }
74
75 fn get_identifier(&self, key: &str) -> Result<[u8; 32], ProtocolError> {
76 self.get_optional_identifier(key)?.ok_or_else(|| {
77 ProtocolError::DecodingError(format!("unable to get identifier property {key}"))
78 })
79 }
80
81 fn get_optional_string(&self, key: &str) -> Result<Option<String>, ProtocolError> {
82 self.get(key)
83 .map(|v| {
84 v.borrow()
85 .as_text()
86 .map(|str| str.to_string())
87 .ok_or_else(|| ProtocolError::DecodingError(format!("{key} must be a string")))
88 })
89 .transpose()
90 }
91
92 fn get_string(&self, key: &str) -> Result<String, ProtocolError> {
93 self.get_optional_string(key)?.ok_or_else(|| {
94 ProtocolError::DecodingError(format!("unable to get string property {key}"))
95 })
96 }
97
98 fn get_optional_str(&self, key: &str) -> Result<Option<&str>, ProtocolError> {
99 self.get(key)
100 .map(|v| {
101 v.borrow()
102 .as_text()
103 .ok_or_else(|| ProtocolError::DecodingError(format!("{key} must be a string")))
104 })
105 .transpose()
106 }
107
108 fn get_str(&self, key: &str) -> Result<&str, ProtocolError> {
109 self.get_optional_str(key)?.ok_or_else(|| {
110 ProtocolError::DecodingError(format!("unable to get str property {key}"))
111 })
112 }
113
114 fn get_optional_integer<T: TryFrom<i128>>(
115 &self,
116 key: &str,
117 ) -> Result<Option<T>, ProtocolError> {
118 self.get(key)
119 .map(|v| {
120 if v.borrow().is_null() {
121 Ok::<Option<Result<T, ProtocolError>>, ProtocolError>(None)
122 } else {
123 Ok(Some(
124 i128::from(v.borrow().as_integer().ok_or_else(|| {
125 ProtocolError::DecodingError(format!("{key} must be an integer"))
126 })?)
127 .try_into()
128 .map_err(|_| {
129 ProtocolError::DecodingError(format!("{key} is out of required bounds"))
130 }),
131 ))
132 }
133 })
134 .transpose()?
135 .flatten()
136 .transpose()
137 }
138
139 fn get_integer<T: TryFrom<i128>>(&self, key: &str) -> Result<T, ProtocolError> {
140 self.get_optional_integer(key)?.ok_or_else(|| {
141 ProtocolError::DecodingError(format!("unable to get integer property {key}"))
142 })
143 }
144
145 fn get_optional_bool(&self, key: &str) -> Result<Option<bool>, ProtocolError> {
146 self.get(key)
147 .map(|v| {
148 v.borrow()
149 .as_bool()
150 .ok_or_else(|| ProtocolError::DecodingError(format!("{key} must be a bool")))
151 })
152 .transpose()
153 }
154
155 fn get_bool(&self, key: &str) -> Result<bool, ProtocolError> {
156 self.get_optional_bool(key)?.ok_or_else(|| {
157 ProtocolError::DecodingError(format!("unable to get bool property {key}"))
158 })
159 }
160
161 fn remove_optional_integer<T: TryFrom<i128>>(
162 &mut self,
163 key: &str,
164 ) -> Result<Option<T>, ProtocolError> {
165 self.remove(key)
166 .map(|v| {
167 if v.borrow().is_null() {
168 Ok::<Option<Result<T, ProtocolError>>, ProtocolError>(None)
169 } else {
170 Ok(Some(
171 i128::from(v.borrow().as_integer().ok_or_else(|| {
172 ProtocolError::DecodingError(format!("{key} must be an integer"))
173 })?)
174 .try_into()
175 .map_err(|_| {
176 ProtocolError::DecodingError(format!("{key} is out of required bounds"))
177 }),
178 ))
179 }
180 })
181 .transpose()?
182 .flatten()
183 .transpose()
184 }
185
186 fn remove_integer<T: TryFrom<i128>>(&mut self, key: &str) -> Result<T, ProtocolError> {
187 self.remove_optional_integer(key)?.ok_or_else(|| {
188 ProtocolError::DecodingError(format!("unable to remove integer property {key}"))
189 })
190 }
191
192 fn get_optional_inner_value_array<'a, I: FromIterator<&'a CborValue>>(
193 &'a self,
194 key: &str,
195 ) -> Result<Option<I>, ProtocolError> {
196 self.get(key)
197 .map(|v| {
198 v.borrow()
199 .as_array()
200 .map(|vec| vec.iter().collect())
201 .ok_or_else(|| ProtocolError::DecodingError(format!("{key} must be a bool")))
202 })
203 .transpose()
204 }
205
206 fn get_inner_value_array<'a, I: FromIterator<&'a CborValue>>(
207 &'a self,
208 key: &str,
209 ) -> Result<I, ProtocolError> {
210 self.get_optional_inner_value_array(key)?.ok_or_else(|| {
211 ProtocolError::DecodingError(format!("unable to get inner value array property {key}"))
212 })
213 }
214
215 fn get_optional_inner_string_array<I: FromIterator<String>>(
216 &self,
217 key: &str,
218 ) -> Result<Option<I>, ProtocolError> {
219 self.get(key)
220 .map(|v| {
221 v.borrow()
222 .as_array()
223 .map(|inner| {
224 inner
225 .iter()
226 .map(|v| {
227 let Some(str) = v.as_text() else {
228 return Err(ProtocolError::DecodingError(format!(
229 "{key} must be an string"
230 )));
231 };
232 Ok(str.to_string())
233 })
234 .collect::<Result<I, ProtocolError>>()
235 })
236 .transpose()?
237 .ok_or_else(|| ProtocolError::DecodingError(format!("{key} must be a bool")))
238 })
239 .transpose()
240 }
241
242 fn get_inner_string_array<I: FromIterator<String>>(
243 &self,
244 key: &str,
245 ) -> Result<I, ProtocolError> {
246 self.get_optional_inner_string_array(key)?.ok_or_else(|| {
247 ProtocolError::DecodingError(format!("unable to get inner string property {key}"))
248 })
249 }
250
251 fn get_optional_inner_borrowed_str_value_map<
252 'a,
253 I: FromIterator<(String, &'a ciborium::Value)>,
254 >(
255 &'a self,
256 key: &str,
257 ) -> Result<Option<I>, ProtocolError> {
258 self.get(key)
259 .map(|v| {
260 v.borrow()
261 .as_map()
262 .map(|inner| {
263 inner
264 .iter()
265 .map(|(k, v)| {
266 let Some(str) = k.as_text() else {
267 return Err(ProtocolError::DecodingError(format!(
268 "{key} must be an string"
269 )));
270 };
271 Ok((str.to_string(), v))
272 })
273 .collect::<Result<I, ProtocolError>>()
274 })
275 .transpose()?
276 .ok_or_else(|| ProtocolError::DecodingError(format!("{key} must be a bool")))
277 })
278 .transpose()
279 }
280
281 fn get_optional_inner_borrowed_map(
282 &self,
283 key: &str,
284 ) -> Result<Option<&Vec<(CborValue, CborValue)>>, ProtocolError> {
285 self.get(key)
286 .map(|v| {
287 v.borrow()
288 .as_map()
289 .ok_or_else(|| ProtocolError::DecodingError(format!("{key} must be a map")))
290 })
291 .transpose()
292 }
293
294 fn get_inner_borrowed_str_value_map<'a, I: FromIterator<(String, &'a ciborium::Value)>>(
295 &'a self,
296 key: &str,
297 ) -> Result<I, ProtocolError> {
298 self.get_optional_inner_borrowed_str_value_map(key)?
299 .ok_or_else(|| {
300 ProtocolError::DecodingError(format!(
301 "unable to get borrowed str value map property {key}"
302 ))
303 })
304 }
305}
306
307#[cfg(test)]
308mod tests {
309 use super::*;
310 use ciborium::value::Value as CborValue;
311 use std::collections::BTreeMap;
312
313 fn make_map(pairs: Vec<(&str, CborValue)>) -> BTreeMap<String, CborValue> {
314 pairs.into_iter().map(|(k, v)| (k.to_string(), v)).collect()
315 }
316
317 #[test]
320 fn get_optional_string_present() {
321 let map = make_map(vec![("name", CborValue::Text("Alice".to_string()))]);
322 let result = map.get_optional_string("name").unwrap();
323 assert_eq!(result, Some("Alice".to_string()));
324 }
325
326 #[test]
327 fn get_optional_string_absent() {
328 let map: BTreeMap<String, CborValue> = BTreeMap::new();
329 let result = map.get_optional_string("name").unwrap();
330 assert_eq!(result, None);
331 }
332
333 #[test]
334 fn get_optional_string_wrong_type() {
335 let map = make_map(vec![("name", CborValue::Integer(42.into()))]);
336 let result = map.get_optional_string("name");
337 assert!(result.is_err());
338 }
339
340 #[test]
341 fn get_string_present() {
342 let map = make_map(vec![("name", CborValue::Text("Bob".to_string()))]);
343 let result = map.get_string("name").unwrap();
344 assert_eq!(result, "Bob");
345 }
346
347 #[test]
348 fn get_string_absent() {
349 let map: BTreeMap<String, CborValue> = BTreeMap::new();
350 let result = map.get_string("name");
351 assert!(result.is_err());
352 }
353
354 #[test]
357 fn get_optional_str_present() {
358 let map = make_map(vec![("key", CborValue::Text("value".to_string()))]);
359 let result = map.get_optional_str("key").unwrap();
360 assert_eq!(result, Some("value"));
361 }
362
363 #[test]
364 fn get_optional_str_absent() {
365 let map: BTreeMap<String, CborValue> = BTreeMap::new();
366 let result = map.get_optional_str("key").unwrap();
367 assert_eq!(result, None);
368 }
369
370 #[test]
371 fn get_str_present() {
372 let map = make_map(vec![("key", CborValue::Text("val".to_string()))]);
373 let result = map.get_str("key").unwrap();
374 assert_eq!(result, "val");
375 }
376
377 #[test]
378 fn get_str_absent() {
379 let map: BTreeMap<String, CborValue> = BTreeMap::new();
380 let result = map.get_str("key");
381 assert!(result.is_err());
382 }
383
384 #[test]
387 fn get_optional_integer_present() {
388 let map = make_map(vec![("count", CborValue::Integer(42.into()))]);
389 let result: Option<i64> = map.get_optional_integer("count").unwrap();
390 assert_eq!(result, Some(42));
391 }
392
393 #[test]
394 fn get_optional_integer_absent() {
395 let map: BTreeMap<String, CborValue> = BTreeMap::new();
396 let result: Option<i64> = map.get_optional_integer("count").unwrap();
397 assert_eq!(result, None);
398 }
399
400 #[test]
401 fn get_optional_integer_null_value() {
402 let map = make_map(vec![("count", CborValue::Null)]);
403 let result: Option<i64> = map.get_optional_integer("count").unwrap();
404 assert_eq!(result, None);
405 }
406
407 #[test]
408 fn get_optional_integer_wrong_type() {
409 let map = make_map(vec![("count", CborValue::Text("not_int".to_string()))]);
410 let result: Result<Option<i64>, _> = map.get_optional_integer("count");
411 assert!(result.is_err());
412 }
413
414 #[test]
415 fn get_integer_present() {
416 let map = make_map(vec![("count", CborValue::Integer(100.into()))]);
417 let result: i64 = map.get_integer("count").unwrap();
418 assert_eq!(result, 100);
419 }
420
421 #[test]
422 fn get_integer_absent() {
423 let map: BTreeMap<String, CborValue> = BTreeMap::new();
424 let result: Result<i64, _> = map.get_integer("count");
425 assert!(result.is_err());
426 }
427
428 #[test]
431 fn get_optional_bool_present() {
432 let map = make_map(vec![("flag", CborValue::Bool(true))]);
433 let result = map.get_optional_bool("flag").unwrap();
434 assert_eq!(result, Some(true));
435 }
436
437 #[test]
438 fn get_optional_bool_absent() {
439 let map: BTreeMap<String, CborValue> = BTreeMap::new();
440 let result = map.get_optional_bool("flag").unwrap();
441 assert_eq!(result, None);
442 }
443
444 #[test]
445 fn get_optional_bool_wrong_type() {
446 let map = make_map(vec![("flag", CborValue::Integer(1.into()))]);
447 let result = map.get_optional_bool("flag");
448 assert!(result.is_err());
449 }
450
451 #[test]
452 fn get_bool_present() {
453 let map = make_map(vec![("flag", CborValue::Bool(false))]);
454 let result = map.get_bool("flag").unwrap();
455 assert!(!result);
456 }
457
458 #[test]
459 fn get_bool_absent() {
460 let map: BTreeMap<String, CborValue> = BTreeMap::new();
461 let result = map.get_bool("flag");
462 assert!(result.is_err());
463 }
464
465 #[test]
468 fn get_optional_identifier_with_bytes() {
469 let id_bytes = [7u8; 32];
470 let map = make_map(vec![("id", CborValue::Bytes(id_bytes.to_vec()))]);
471 let result = map.get_optional_identifier("id").unwrap();
472 assert_eq!(result, Some(id_bytes));
473 }
474
475 #[test]
476 fn get_optional_identifier_absent() {
477 let map: BTreeMap<String, CborValue> = BTreeMap::new();
478 let result = map.get_optional_identifier("id").unwrap();
479 assert_eq!(result, None);
480 }
481
482 #[test]
483 fn get_identifier_present() {
484 let id_bytes = [3u8; 32];
485 let map = make_map(vec![("id", CborValue::Bytes(id_bytes.to_vec()))]);
486 let result = map.get_identifier("id").unwrap();
487 assert_eq!(result, id_bytes);
488 }
489
490 #[test]
491 fn get_identifier_absent() {
492 let map: BTreeMap<String, CborValue> = BTreeMap::new();
493 let result = map.get_identifier("id");
494 assert!(result.is_err());
495 }
496
497 #[test]
500 fn remove_optional_integer_present() {
501 let mut map = make_map(vec![("val", CborValue::Integer(99.into()))]);
502 let result: Option<i64> = map.remove_optional_integer("val").unwrap();
503 assert_eq!(result, Some(99));
504 assert!(!map.contains_key("val"));
505 }
506
507 #[test]
508 fn remove_optional_integer_absent() {
509 let mut map: BTreeMap<String, CborValue> = BTreeMap::new();
510 let result: Option<i64> = map.remove_optional_integer("val").unwrap();
511 assert_eq!(result, None);
512 }
513
514 #[test]
515 fn remove_optional_integer_null() {
516 let mut map = make_map(vec![("val", CborValue::Null)]);
517 let result: Option<i64> = map.remove_optional_integer("val").unwrap();
518 assert_eq!(result, None);
519 }
520
521 #[test]
522 fn remove_integer_present() {
523 let mut map = make_map(vec![("val", CborValue::Integer(50.into()))]);
524 let result: i64 = map.remove_integer("val").unwrap();
525 assert_eq!(result, 50);
526 }
527
528 #[test]
529 fn remove_integer_absent() {
530 let mut map: BTreeMap<String, CborValue> = BTreeMap::new();
531 let result: Result<i64, _> = map.remove_integer("val");
532 assert!(result.is_err());
533 }
534
535 #[test]
538 fn get_optional_inner_value_array_present() {
539 let array = vec![CborValue::Integer(1.into()), CborValue::Integer(2.into())];
540 let map = make_map(vec![("arr", CborValue::Array(array))]);
541 let result: Option<Vec<&CborValue>> = map.get_optional_inner_value_array("arr").unwrap();
542 assert!(result.is_some());
543 assert_eq!(result.unwrap().len(), 2);
544 }
545
546 #[test]
547 fn get_optional_inner_value_array_absent() {
548 let map: BTreeMap<String, CborValue> = BTreeMap::new();
549 let result: Option<Vec<&CborValue>> = map.get_optional_inner_value_array("arr").unwrap();
550 assert!(result.is_none());
551 }
552
553 #[test]
554 fn get_inner_value_array_present() {
555 let array = vec![CborValue::Bool(true)];
556 let map = make_map(vec![("arr", CborValue::Array(array))]);
557 let result: Vec<&CborValue> = map.get_inner_value_array("arr").unwrap();
558 assert_eq!(result.len(), 1);
559 }
560
561 #[test]
562 fn get_inner_value_array_absent() {
563 let map: BTreeMap<String, CborValue> = BTreeMap::new();
564 let result: Result<Vec<&CborValue>, _> = map.get_inner_value_array("arr");
565 assert!(result.is_err());
566 }
567
568 #[test]
571 fn get_optional_inner_string_array_present() {
572 let array = vec![
573 CborValue::Text("hello".to_string()),
574 CborValue::Text("world".to_string()),
575 ];
576 let map = make_map(vec![("strs", CborValue::Array(array))]);
577 let result: Option<Vec<String>> = map.get_optional_inner_string_array("strs").unwrap();
578 assert_eq!(result, Some(vec!["hello".to_string(), "world".to_string()]));
579 }
580
581 #[test]
582 fn get_optional_inner_string_array_with_non_string_element() {
583 let array = vec![
584 CborValue::Text("hello".to_string()),
585 CborValue::Integer(42.into()),
586 ];
587 let map = make_map(vec![("strs", CborValue::Array(array))]);
588 let result: Result<Option<Vec<String>>, _> = map.get_optional_inner_string_array("strs");
589 assert!(result.is_err());
590 }
591
592 #[test]
593 fn get_inner_string_array_absent() {
594 let map: BTreeMap<String, CborValue> = BTreeMap::new();
595 let result: Result<Vec<String>, _> = map.get_inner_string_array("strs");
596 assert!(result.is_err());
597 }
598
599 #[test]
602 fn get_optional_inner_borrowed_map_present() {
603 let inner_map = vec![(
604 CborValue::Text("k".to_string()),
605 CborValue::Integer(1.into()),
606 )];
607 let map = make_map(vec![("m", CborValue::Map(inner_map))]);
608 let result = map.get_optional_inner_borrowed_map("m").unwrap();
609 assert!(result.is_some());
610 assert_eq!(result.unwrap().len(), 1);
611 }
612
613 #[test]
614 fn get_optional_inner_borrowed_map_absent() {
615 let map: BTreeMap<String, CborValue> = BTreeMap::new();
616 let result = map.get_optional_inner_borrowed_map("m").unwrap();
617 assert!(result.is_none());
618 }
619
620 #[test]
621 fn get_optional_inner_borrowed_map_wrong_type() {
622 let map = make_map(vec![("m", CborValue::Integer(1.into()))]);
623 let result = map.get_optional_inner_borrowed_map("m");
624 assert!(result.is_err());
625 }
626
627 #[test]
630 fn get_optional_inner_borrowed_str_value_map_present() {
631 let inner_map = vec![(
632 CborValue::Text("key".to_string()),
633 CborValue::Integer(42.into()),
634 )];
635 let map = make_map(vec![("m", CborValue::Map(inner_map))]);
636 let result: Option<BTreeMap<String, &CborValue>> =
637 map.get_optional_inner_borrowed_str_value_map("m").unwrap();
638 assert!(result.is_some());
639 let inner = result.unwrap();
640 assert_eq!(inner.len(), 1);
641 assert!(inner.contains_key("key"));
642 }
643
644 #[test]
645 fn get_inner_borrowed_str_value_map_absent() {
646 let map: BTreeMap<String, CborValue> = BTreeMap::new();
647 let result: Result<BTreeMap<String, &CborValue>, _> =
648 map.get_inner_borrowed_str_value_map("m");
649 assert!(result.is_err());
650 }
651}