1use std::{
2 cmp::Ordering,
3 collections::BTreeMap,
4 convert::{TryFrom, TryInto},
5};
6
7use ciborium::value::Value as CborValue;
8
9use serde::Serialize;
10
11use crate::ProtocolError;
12
13use super::{
14 convert::convert_to, get_from_cbor_map, to_path_of_cbors, FieldType, ReplacePaths,
15 ValuesCollection,
16};
17
18#[derive(Default, Clone, Debug)]
19pub struct CborCanonicalMap {
20 inner: Vec<(CborValue, CborValue)>,
21}
22
23impl CborCanonicalMap {
24 pub fn new() -> Self {
25 Self { inner: vec![] }
26 }
27
28 pub fn from_serializable<T>(value: &T) -> Result<Self, ProtocolError>
29 where
30 T: Serialize,
31 {
32 let cbor = ciborium::value::Value::serialized(&value)
33 .map_err(|e| ProtocolError::EncodingError(e.to_string()))?;
34 CborCanonicalMap::try_from(cbor).map_err(|e| ProtocolError::EncodingError(e.to_string()))
35 }
36
37 pub fn from_vector(vec: Vec<(CborValue, CborValue)>) -> Self {
38 let mut map = Self::new();
39 map.inner = vec;
40 map
41 }
42
43 pub fn insert(&mut self, key: impl Into<String>, value: impl Into<CborValue>) {
44 self.inner.push((CborValue::Text(key.into()), value.into()));
45 }
46
47 pub fn remove(&mut self, key_to_remove: impl Into<CborValue>) {
48 let key_to_compare: CborValue = key_to_remove.into();
49 if let Some(index) = self
50 .inner
51 .iter()
52 .position(|(key, _)| key == &key_to_compare)
53 {
54 self.inner.remove(index);
55 }
56 }
57
58 pub fn get_mut(&mut self, key: &CborValue) -> Option<&mut CborValue> {
59 if let Some(index) = self.inner.iter().position(|(el_key, _)| el_key == key) {
60 Some(&mut self.inner.get_mut(index)?.1)
61 } else {
62 None
63 }
64 }
65
66 pub fn replace_paths<I, C>(&mut self, paths: I, from: FieldType, to: FieldType)
67 where
68 I: IntoIterator<Item = C>,
69 C: AsRef<str>,
70 {
71 for path in paths.into_iter() {
72 self.replace_path(path.as_ref(), from, to);
73 }
74 }
75
76 pub fn replace_path(&mut self, path: &str, from: FieldType, to: FieldType) -> Option<()> {
77 let cbor_value = self.get_path_mut(path)?;
78 let replace_with = convert_to(cbor_value, from, to)?;
79
80 *cbor_value = replace_with;
81
82 Some(())
83 }
84
85 pub fn set(&mut self, key: &CborValue, replace_with: CborValue) -> Option<()> {
86 if let Some(index) = self.inner.iter().position(|(el_key, _)| el_key == key) {
87 if let Some(key_value) = self.inner.get_mut(index) {
88 key_value.1 = replace_with;
89 Some(())
90 } else {
91 None
92 }
93 } else {
94 None
95 }
96 }
97
98 pub fn sort_canonical(&mut self) {
107 recursively_sort_canonical_cbor_map(&mut self.inner)
108 }
109
110 pub fn to_bytes(mut self) -> Result<Vec<u8>, ciborium::ser::Error<std::io::Error>> {
111 self.sort_canonical();
112
113 let mut bytes = Vec::<u8>::new();
114
115 let map = CborValue::Map(self.inner);
116
117 ciborium::ser::into_writer(&map, &mut bytes)?;
118
119 Ok(bytes)
120 }
121
122 pub fn to_value_unsorted(&self) -> CborValue {
123 CborValue::Map(self.inner.clone())
124 }
125
126 pub fn to_value_sorted(mut self) -> CborValue {
127 self.sort_canonical();
128
129 CborValue::Map(self.inner)
130 }
131
132 pub fn to_value_clone(&mut self) -> CborValue {
133 self.sort_canonical();
134
135 CborValue::Map(self.inner.clone())
136 }
137}
138
139impl ValuesCollection for CborCanonicalMap {
140 type Key = CborValue;
141 type Value = CborValue;
142
143 fn get(&self, key: &Self::Key) -> Option<&Self::Value> {
144 if let Some(index) = self.inner.iter().position(|(el_key, _)| el_key == key) {
145 Some(&self.inner.get(index)?.1)
146 } else {
147 None
148 }
149 }
150
151 fn get_mut(&mut self, key: &CborValue) -> Option<&mut CborValue> {
152 if let Some(index) = self.inner.iter().position(|(el_key, _)| el_key == key) {
153 Some(&mut self.inner.get_mut(index)?.1)
154 } else {
155 None
156 }
157 }
158
159 fn remove(&mut self, key_to_remove: impl Into<CborValue>) -> Option<Self::Value> {
160 let key_to_compare: CborValue = key_to_remove.into();
161 if let Some(index) = self
162 .inner
163 .iter()
164 .position(|(key, _)| key == &key_to_compare)
165 {
166 let (_, v) = self.inner.remove(index);
167 Some(v)
168 } else {
169 None
170 }
171 }
172}
173
174impl ReplacePaths for CborCanonicalMap {
175 type Value = CborValue;
176
177 fn replace_paths<I, C>(&mut self, paths: I, from: FieldType, to: FieldType)
178 where
179 I: IntoIterator<Item = C>,
180 C: AsRef<str>,
181 {
182 for path in paths.into_iter() {
183 self.replace_path(path.as_ref(), from, to);
184 }
185 }
186
187 fn replace_path(&mut self, path: &str, from: FieldType, to: FieldType) -> Option<()> {
188 let cbor_value = self.get_path_mut(path)?;
189 let replace_with = convert_to(cbor_value, from, to)?;
190
191 *cbor_value = replace_with;
192
193 Some(())
194 }
195
196 fn get_path_mut(&mut self, path: &str) -> Option<&mut CborValue> {
197 let cbor_path = to_path_of_cbors(path).ok()?;
198 if cbor_path.is_empty() {
199 return None;
200 }
201 if cbor_path.len() == 1 {
202 return self.get_mut(&cbor_path[0]);
203 }
204
205 let mut current_level: &mut CborValue = self.get_mut(&cbor_path[0])?;
206 for step in cbor_path.iter().skip(1) {
207 match current_level {
208 CborValue::Map(ref mut cbor_map) => {
209 current_level = get_from_cbor_map(cbor_map, step)?
210 }
211 CborValue::Array(ref mut cbor_array) => {
212 if let Some(idx) = step.as_integer() {
213 let id: usize = idx.try_into().ok()?;
214 current_level = cbor_array.get_mut(id)?
215 } else {
216 return None;
217 }
218 }
219 _ => {
220 }
222 }
223 }
224 Some(current_level)
225 }
226}
227
228impl TryFrom<CborValue> for CborCanonicalMap {
229 type Error = ProtocolError;
230
231 fn try_from(value: CborValue) -> Result<Self, Self::Error> {
232 if let CborValue::Map(map) = value {
233 Ok(Self::from_vector(map))
234 } else {
235 Err(ProtocolError::ParsingError(
236 "Expected map to be a map".into(),
237 ))
238 }
239 }
240}
241
242impl From<Vec<(CborValue, CborValue)>> for CborCanonicalMap {
243 fn from(vec: Vec<(CborValue, CborValue)>) -> Self {
244 Self::from_vector(vec)
245 }
246}
247
248impl From<&Vec<(CborValue, CborValue)>> for CborCanonicalMap {
249 fn from(vec: &Vec<(CborValue, CborValue)>) -> Self {
250 Self::from_vector(vec.clone())
251 }
252}
253
254impl<T> From<&BTreeMap<String, T>> for CborCanonicalMap
255where
256 T: Into<CborValue> + Clone,
257{
258 fn from(map: &BTreeMap<String, T>) -> Self {
259 let vec = map
260 .iter()
261 .map(|(key, value)| (key.clone().into(), value.clone().into()))
262 .collect::<Vec<(CborValue, CborValue)>>();
263
264 Self::from(vec)
265 }
266}
267
268fn recursively_sort_canonical_cbor_map(cbor_map: &mut [(CborValue, CborValue)]) {
269 for (_, value) in cbor_map.iter_mut() {
270 if let CborValue::Map(map) = value {
271 recursively_sort_canonical_cbor_map(map)
272 }
273 if let CborValue::Array(array) = value {
274 for item in array.iter_mut() {
275 if let CborValue::Map(map) = item {
276 recursively_sort_canonical_cbor_map(map)
277 }
278 }
279 }
280 }
281
282 cbor_map.sort_by(|a, b| {
283 let key_a = a.0.as_text().unwrap().as_bytes();
286 let key_b = b.0.as_text().unwrap().as_bytes();
287
288 let len_comparison = key_a.len().cmp(&key_b.len());
289
290 match len_comparison {
291 Ordering::Less => Ordering::Less,
292 Ordering::Equal => key_a.cmp(key_b),
293 Ordering::Greater => Ordering::Greater,
294 }
295 });
296}
297
298pub fn value_to_bytes(value: &CborValue) -> Result<Option<Vec<u8>>, ProtocolError> {
300 match value {
301 CborValue::Bytes(bytes) => Ok(Some(bytes.clone())),
302 CborValue::Text(text) => match bs58::decode(text).into_vec() {
303 Ok(data) => Ok(Some(data)),
304 Err(_) => Ok(None),
305 },
306 CborValue::Array(array) => array
307 .iter()
308 .map(|byte| match byte {
309 CborValue::Integer(int) => {
310 let value_as_u8: u8 = (*int).try_into().map_err(|_| {
311 ProtocolError::DecodingError(String::from("expected u8 value"))
312 })?;
313 Ok(Some(value_as_u8))
314 }
315 _ => Err(ProtocolError::DecodingError(String::from(
316 "not an array of integers",
317 ))),
318 })
319 .collect::<Result<Option<Vec<u8>>, ProtocolError>>(),
320 _ => Err(ProtocolError::DecodingError(String::from(
321 "system value is incorrect type",
322 ))),
323 }
324}
325
326pub fn value_to_hash(value: &CborValue) -> Result<[u8; 32], ProtocolError> {
327 match value {
328 CborValue::Bytes(bytes) => bytes
329 .clone()
330 .try_into()
331 .map_err(|_| ProtocolError::DecodingError("expected 32 bytes".to_string())),
332 CborValue::Text(text) => match bs58::decode(text).into_vec() {
333 Ok(bytes) => bytes
334 .try_into()
335 .map_err(|_| ProtocolError::DecodingError("expected 32 bytes".to_string())),
336 Err(_) => Err(ProtocolError::DecodingError(
337 "expected 32 bytes".to_string(),
338 )),
339 },
340 CborValue::Array(array) => array
341 .iter()
342 .map(|byte| match byte {
343 CborValue::Integer(int) => {
344 let value_as_u8: u8 = (*int).try_into().map_err(|_| {
345 ProtocolError::DecodingError(String::from("expected u8 value"))
346 })?;
347 Ok(value_as_u8)
348 }
349 _ => Err(ProtocolError::DecodingError(String::from(
350 "not an array of integers",
351 ))),
352 })
353 .collect::<Result<Vec<u8>, ProtocolError>>()?
354 .try_into()
355 .map_err(|_| ProtocolError::DecodingError("expected 32 bytes".to_string())),
356 _ => Err(ProtocolError::DecodingError(String::from(
357 "system value is incorrect type",
358 ))),
359 }
360}
361
362#[cfg(test)]
363mod test {
364 use std::collections::BTreeMap;
365 use std::convert::TryFrom;
366 use std::convert::TryInto;
367
368 use crate::util::cbor_value::{ReplacePaths, ValuesCollection};
369
370 use super::{value_to_bytes, value_to_hash, CborCanonicalMap, CborValue, FieldType};
371 use ciborium::cbor;
372
373 #[test]
376 fn should_get_path_to_property_from_cbor() {
377 let cbor_value = cbor!( {
378 "alpha" => {
379 "bravo" => "bravo_value",
380 }
381 })
382 .expect("valid cbor");
383 let mut canonical: CborCanonicalMap = cbor_value.try_into().expect("valid canonical");
384 let result = canonical.get_path_mut("alpha.bravo").expect("bravo value");
385 assert_eq!(&mut CborValue::Text(String::from("bravo_value")), result);
386 }
387
388 #[test]
389 fn should_get_paths_to_array_from_cbor() {
390 let cbor_value = cbor!( {
391 "alpha" => {
392 "bravo" => ["bravo_first_item", "bravo_second_item" ],
393 }
394 })
395 .expect("valid cbor");
396 let mut canonical: CborCanonicalMap = cbor_value.try_into().expect("valid canonical");
397 let result = canonical
398 .get_path_mut("alpha.bravo[0]")
399 .expect("first item from bravo");
400 assert_eq!(
401 &mut CborValue::Text(String::from("bravo_first_item")),
402 result
403 );
404 }
405
406 #[test]
407 fn should_return_non_when_path_not_exist() {
408 let cbor_value = cbor!( {
409 "alpha" => {
410 "bravo" => ["bravo_first_item", "bravo_second_item" ],
411 }
412 })
413 .expect("valid cbor");
414 let mut canonical: CborCanonicalMap = cbor_value.try_into().expect("valid canonical");
415 let path = "alpha.bravo[-1]";
416
417 assert!(canonical.get_path_mut(path).is_none())
418 }
419
420 #[test]
421 fn should_replace_cbor_value() {
422 let cbor_value = cbor!({
423 "alpha" => {
424 "array_value" => vec![0_u8;32]
425
426 }
427 })
428 .expect("cbor should be created");
429
430 let mut canonical: CborCanonicalMap = cbor_value.try_into().expect("valid canonical");
431 canonical.replace_path(
432 "alpha.array_value",
433 FieldType::ArrayInt,
434 FieldType::StringBase58,
435 );
436
437 let replaced = canonical
438 .get_path_mut("alpha.array_value")
439 .expect("value should be returned");
440
441 assert_eq!(
442 &mut CborValue::Text(bs58::encode(vec![0_u8; 32]).into_string()),
443 replaced
444 );
445 }
446
447 #[test]
452 fn new_creates_empty_map() {
453 let map = CborCanonicalMap::new();
454 let value = map.to_value_unsorted();
455 assert_eq!(value, CborValue::Map(vec![]));
456 }
457
458 #[test]
459 fn default_creates_empty_map() {
460 let map = CborCanonicalMap::default();
461 let value = map.to_value_unsorted();
462 assert_eq!(value, CborValue::Map(vec![]));
463 }
464
465 #[test]
466 fn insert_adds_key_value_pair() {
467 let mut map = CborCanonicalMap::new();
468 map.insert("hello", CborValue::Text("world".to_string()));
469
470 let val = ValuesCollection::get(&map, &CborValue::Text("hello".to_string()));
471 assert_eq!(val, Some(&CborValue::Text("world".to_string())));
472 }
473
474 #[test]
475 fn insert_multiple_keys() {
476 let mut map = CborCanonicalMap::new();
477 map.insert("a", CborValue::Integer(1.into()));
478 map.insert("b", CborValue::Integer(2.into()));
479 map.insert("c", CborValue::Integer(3.into()));
480
481 assert_eq!(
482 ValuesCollection::get(&map, &CborValue::Text("a".to_string())),
483 Some(&CborValue::Integer(1.into()))
484 );
485 assert_eq!(
486 ValuesCollection::get(&map, &CborValue::Text("b".to_string())),
487 Some(&CborValue::Integer(2.into()))
488 );
489 assert_eq!(
490 ValuesCollection::get(&map, &CborValue::Text("c".to_string())),
491 Some(&CborValue::Integer(3.into()))
492 );
493 }
494
495 #[test]
496 fn remove_existing_key() {
497 let mut map = CborCanonicalMap::new();
498 map.insert("key1", CborValue::Bool(true));
499 map.insert("key2", CborValue::Bool(false));
500
501 map.remove("key1");
502
503 assert!(ValuesCollection::get(&map, &CborValue::Text("key1".to_string())).is_none());
504 assert_eq!(
505 ValuesCollection::get(&map, &CborValue::Text("key2".to_string())),
506 Some(&CborValue::Bool(false))
507 );
508 }
509
510 #[test]
511 fn remove_nonexistent_key_is_noop() {
512 let mut map = CborCanonicalMap::new();
513 map.insert("key1", CborValue::Bool(true));
514
515 map.remove("nonexistent");
517
518 assert_eq!(
519 ValuesCollection::get(&map, &CborValue::Text("key1".to_string())),
520 Some(&CborValue::Bool(true))
521 );
522 }
523
524 #[test]
525 fn get_mut_returns_mutable_reference() {
526 let mut map = CborCanonicalMap::new();
527 map.insert("key", CborValue::Integer(10.into()));
528
529 let val = map.get_mut(&CborValue::Text("key".to_string()));
530 assert!(val.is_some());
531 *val.unwrap() = CborValue::Integer(20.into());
532
533 assert_eq!(
534 ValuesCollection::get(&map, &CborValue::Text("key".to_string())),
535 Some(&CborValue::Integer(20.into()))
536 );
537 }
538
539 #[test]
540 fn get_mut_returns_none_for_missing_key() {
541 let mut map = CborCanonicalMap::new();
542 assert!(map
543 .get_mut(&CborValue::Text("missing".to_string()))
544 .is_none());
545 }
546
547 #[test]
548 fn set_replaces_existing_value() {
549 let mut map = CborCanonicalMap::new();
550 map.insert("key", CborValue::Integer(1.into()));
551
552 let result = map.set(
553 &CborValue::Text("key".to_string()),
554 CborValue::Integer(99.into()),
555 );
556 assert!(result.is_some());
557
558 assert_eq!(
559 ValuesCollection::get(&map, &CborValue::Text("key".to_string())),
560 Some(&CborValue::Integer(99.into()))
561 );
562 }
563
564 #[test]
565 fn set_returns_none_for_missing_key() {
566 let mut map = CborCanonicalMap::new();
567
568 let result = map.set(
569 &CborValue::Text("missing".to_string()),
570 CborValue::Integer(1.into()),
571 );
572 assert!(result.is_none());
573 }
574
575 #[test]
576 fn from_vector_creates_map() {
577 let vec = vec![
578 (
579 CborValue::Text("a".to_string()),
580 CborValue::Integer(1.into()),
581 ),
582 (
583 CborValue::Text("b".to_string()),
584 CborValue::Integer(2.into()),
585 ),
586 ];
587
588 let map = CborCanonicalMap::from_vector(vec);
589
590 assert_eq!(
591 ValuesCollection::get(&map, &CborValue::Text("a".to_string())),
592 Some(&CborValue::Integer(1.into()))
593 );
594 assert_eq!(
595 ValuesCollection::get(&map, &CborValue::Text("b".to_string())),
596 Some(&CborValue::Integer(2.into()))
597 );
598 }
599
600 #[test]
601 fn from_serializable_with_btreemap() {
602 let mut btree = BTreeMap::new();
603 btree.insert("name".to_string(), "test".to_string());
604
605 let map =
606 CborCanonicalMap::from_serializable(&btree).expect("should serialize from BTreeMap");
607
608 assert!(ValuesCollection::get(&map, &CborValue::Text("name".to_string())).is_some());
609 }
610
611 #[test]
612 fn from_serializable_with_non_map_value_fails() {
613 let result = CborCanonicalMap::from_serializable(&"just a string");
615 assert!(result.is_err());
616 }
617
618 #[test]
621 fn sort_canonical_orders_by_key_length_then_lexicographic() {
622 let mut map = CborCanonicalMap::new();
623 map.insert("beta", CborValue::Integer(2.into()));
625 map.insert("a", CborValue::Integer(1.into()));
626 map.insert("cc", CborValue::Integer(3.into()));
627 map.insert("bb", CborValue::Integer(4.into()));
628
629 map.sort_canonical();
630
631 let sorted = map.to_value_unsorted();
632 if let CborValue::Map(pairs) = sorted {
633 let keys: Vec<&str> = pairs.iter().map(|(k, _)| k.as_text().unwrap()).collect();
634 assert_eq!(keys, vec!["a", "bb", "cc", "beta"]);
636 } else {
637 panic!("Expected map");
638 }
639 }
640
641 #[test]
642 fn sort_canonical_recursively_sorts_nested_maps() {
643 let mut map = CborCanonicalMap::new();
644 let nested = CborValue::Map(vec![
646 (
647 CborValue::Text("zz".to_string()),
648 CborValue::Integer(1.into()),
649 ),
650 (
651 CborValue::Text("a".to_string()),
652 CborValue::Integer(2.into()),
653 ),
654 ]);
655 map.insert("outer", nested);
656
657 map.sort_canonical();
658
659 let sorted = map.to_value_unsorted();
660 if let CborValue::Map(pairs) = sorted {
661 if let CborValue::Map(inner_pairs) = &pairs[0].1 {
662 let keys: Vec<&str> = inner_pairs
663 .iter()
664 .map(|(k, _)| k.as_text().unwrap())
665 .collect();
666 assert_eq!(keys, vec!["a", "zz"]);
668 } else {
669 panic!("Expected nested map");
670 }
671 }
672 }
673
674 #[test]
675 fn sort_canonical_recursively_sorts_maps_inside_arrays() {
676 let mut map = CborCanonicalMap::new();
677 let nested_map_in_array = CborValue::Array(vec![CborValue::Map(vec![
678 (
679 CborValue::Text("zz".to_string()),
680 CborValue::Integer(1.into()),
681 ),
682 (
683 CborValue::Text("a".to_string()),
684 CborValue::Integer(2.into()),
685 ),
686 ])]);
687 map.insert("items", nested_map_in_array);
688
689 map.sort_canonical();
690
691 let sorted = map.to_value_unsorted();
692 if let CborValue::Map(pairs) = sorted {
693 if let CborValue::Array(arr) = &pairs[0].1 {
694 if let CborValue::Map(inner_pairs) = &arr[0] {
695 let keys: Vec<&str> = inner_pairs
696 .iter()
697 .map(|(k, _)| k.as_text().unwrap())
698 .collect();
699 assert_eq!(keys, vec!["a", "zz"]);
700 } else {
701 panic!("Expected map inside array");
702 }
703 } else {
704 panic!("Expected array");
705 }
706 }
707 }
708
709 #[test]
712 fn to_bytes_produces_valid_cbor() {
713 let mut map = CborCanonicalMap::new();
714 map.insert("key", CborValue::Text("value".to_string()));
715
716 let bytes = map.to_bytes().expect("should serialize to bytes");
717 assert!(!bytes.is_empty());
718
719 let deserialized: CborValue =
721 ciborium::de::from_reader(&bytes[..]).expect("should deserialize");
722 if let CborValue::Map(pairs) = deserialized {
723 assert_eq!(pairs.len(), 1);
724 assert_eq!(
725 pairs[0],
726 (
727 CborValue::Text("key".to_string()),
728 CborValue::Text("value".to_string())
729 )
730 );
731 } else {
732 panic!("Expected map after deserialization");
733 }
734 }
735
736 #[test]
737 fn to_bytes_sorts_before_serializing() {
738 let mut map = CborCanonicalMap::new();
739 map.insert("beta", CborValue::Integer(2.into()));
740 map.insert("a", CborValue::Integer(1.into()));
741
742 let bytes = map.to_bytes().expect("should serialize");
743 let deserialized: CborValue =
744 ciborium::de::from_reader(&bytes[..]).expect("should deserialize");
745
746 if let CborValue::Map(pairs) = deserialized {
747 let keys: Vec<&str> = pairs.iter().map(|(k, _)| k.as_text().unwrap()).collect();
748 assert_eq!(keys, vec!["a", "beta"]);
749 }
750 }
751
752 #[test]
753 fn to_value_unsorted_preserves_insertion_order() {
754 let mut map = CborCanonicalMap::new();
755 map.insert("z", CborValue::Integer(1.into()));
756 map.insert("a", CborValue::Integer(2.into()));
757
758 let value = map.to_value_unsorted();
759 if let CborValue::Map(pairs) = value {
760 assert_eq!(pairs[0].0, CborValue::Text("z".to_string()));
761 assert_eq!(pairs[1].0, CborValue::Text("a".to_string()));
762 }
763 }
764
765 #[test]
766 fn to_value_sorted_returns_sorted_map() {
767 let mut map = CborCanonicalMap::new();
768 map.insert("beta", CborValue::Integer(2.into()));
769 map.insert("a", CborValue::Integer(1.into()));
770
771 let value = map.to_value_sorted();
772 if let CborValue::Map(pairs) = value {
773 assert_eq!(pairs[0].0, CborValue::Text("a".to_string()));
774 assert_eq!(pairs[1].0, CborValue::Text("beta".to_string()));
775 }
776 }
777
778 #[test]
779 fn to_value_clone_returns_sorted_clone() {
780 let mut map = CborCanonicalMap::new();
781 map.insert("beta", CborValue::Integer(2.into()));
782 map.insert("a", CborValue::Integer(1.into()));
783
784 let value = map.to_value_clone();
785 if let CborValue::Map(pairs) = value {
786 assert_eq!(pairs[0].0, CborValue::Text("a".to_string()));
787 assert_eq!(pairs[1].0, CborValue::Text("beta".to_string()));
788 }
789
790 let val = ValuesCollection::get(&map, &CborValue::Text("a".to_string()));
792 assert!(val.is_some());
793 }
794
795 #[test]
798 fn try_from_cbor_map_succeeds() {
799 let cbor = CborValue::Map(vec![(
800 CborValue::Text("k".to_string()),
801 CborValue::Bool(true),
802 )]);
803
804 let map = CborCanonicalMap::try_from(cbor).expect("should convert from map");
805 assert_eq!(
806 ValuesCollection::get(&map, &CborValue::Text("k".to_string())),
807 Some(&CborValue::Bool(true))
808 );
809 }
810
811 #[test]
812 fn try_from_non_map_fails() {
813 let cbor = CborValue::Text("not a map".to_string());
814 let result = CborCanonicalMap::try_from(cbor);
815 assert!(result.is_err());
816 }
817
818 #[test]
819 fn from_vec_creates_canonical_map() {
820 let vec = vec![(
821 CborValue::Text("x".to_string()),
822 CborValue::Integer(42.into()),
823 )];
824
825 let map: CborCanonicalMap = vec.into();
826 assert_eq!(
827 ValuesCollection::get(&map, &CborValue::Text("x".to_string())),
828 Some(&CborValue::Integer(42.into()))
829 );
830 }
831
832 #[test]
833 fn from_ref_vec_creates_canonical_map() {
834 let vec = vec![(
835 CborValue::Text("y".to_string()),
836 CborValue::Integer(7.into()),
837 )];
838
839 let map: CborCanonicalMap = (&vec).into();
840 assert_eq!(
841 ValuesCollection::get(&map, &CborValue::Text("y".to_string())),
842 Some(&CborValue::Integer(7.into()))
843 );
844 }
845
846 #[test]
847 fn from_btreemap_string_creates_canonical_map() {
848 let mut btree = BTreeMap::new();
849 btree.insert("alpha".to_string(), CborValue::Integer(1.into()));
850 btree.insert("beta".to_string(), CborValue::Integer(2.into()));
851
852 let map: CborCanonicalMap = (&btree).into();
853 assert_eq!(
854 ValuesCollection::get(&map, &CborValue::Text("alpha".to_string())),
855 Some(&CborValue::Integer(1.into()))
856 );
857 assert_eq!(
858 ValuesCollection::get(&map, &CborValue::Text("beta".to_string())),
859 Some(&CborValue::Integer(2.into()))
860 );
861 }
862
863 #[test]
866 fn values_collection_get_returns_value() {
867 let map = CborCanonicalMap::from_vector(vec![(
868 CborValue::Text("k".to_string()),
869 CborValue::Text("v".to_string()),
870 )]);
871
872 let result = ValuesCollection::get(&map, &CborValue::Text("k".to_string()));
873 assert_eq!(result, Some(&CborValue::Text("v".to_string())));
874 }
875
876 #[test]
877 fn values_collection_get_returns_none_for_missing() {
878 let map = CborCanonicalMap::new();
879 let result = ValuesCollection::get(&map, &CborValue::Text("missing".to_string()));
880 assert!(result.is_none());
881 }
882
883 #[test]
884 fn values_collection_remove_returns_removed_value() {
885 let mut map = CborCanonicalMap::from_vector(vec![
886 (
887 CborValue::Text("a".to_string()),
888 CborValue::Integer(1.into()),
889 ),
890 (
891 CborValue::Text("b".to_string()),
892 CborValue::Integer(2.into()),
893 ),
894 ]);
895
896 let removed = ValuesCollection::remove(&mut map, "a");
897 assert_eq!(removed, Some(CborValue::Integer(1.into())));
898 assert!(ValuesCollection::get(&map, &CborValue::Text("a".to_string())).is_none());
899 }
900
901 #[test]
902 fn values_collection_remove_returns_none_for_missing() {
903 let mut map = CborCanonicalMap::new();
904 let removed = ValuesCollection::remove(&mut map, "nonexistent");
905 assert!(removed.is_none());
906 }
907
908 #[test]
911 fn replace_paths_converts_multiple_paths() {
912 let cbor_value = cbor!({
913 "field1" => vec![0_u8; 32],
914 "field2" => vec![1_u8; 32]
915 })
916 .expect("valid cbor");
917
918 let mut canonical: CborCanonicalMap = cbor_value.try_into().expect("valid canonical");
919 ReplacePaths::replace_paths(
920 &mut canonical,
921 vec!["field1", "field2"],
922 FieldType::ArrayInt,
923 FieldType::Bytes,
924 );
925
926 let v1 = ValuesCollection::get(&canonical, &CborValue::Text("field1".to_string()));
927 assert!(matches!(v1, Some(CborValue::Bytes(_))));
928 let v2 = ValuesCollection::get(&canonical, &CborValue::Text("field2".to_string()));
929 assert!(matches!(v2, Some(CborValue::Bytes(_))));
930 }
931
932 #[test]
933 fn replace_path_returns_none_for_nonexistent_path() {
934 let mut map = CborCanonicalMap::new();
935 map.insert("exists", CborValue::Text("value".to_string()));
936
937 let result =
938 ReplacePaths::replace_path(&mut map, "nonexistent", FieldType::Bytes, FieldType::Bytes);
939 assert!(result.is_none());
940 }
941
942 #[test]
943 fn get_path_mut_with_empty_path_returns_none() {
944 let mut map = CborCanonicalMap::new();
945 map.insert("key", CborValue::Integer(1.into()));
946
947 let result = ReplacePaths::get_path_mut(&mut map, "");
950 assert!(result.is_none());
953 }
954
955 #[test]
958 fn value_to_bytes_from_bytes() {
959 let val = CborValue::Bytes(vec![1, 2, 3, 4]);
960 let result = value_to_bytes(&val).expect("should succeed");
961 assert_eq!(result, Some(vec![1, 2, 3, 4]));
962 }
963
964 #[test]
965 fn value_to_bytes_from_valid_base58_text() {
966 let original = vec![1, 2, 3, 4, 5];
967 let encoded = bs58::encode(&original).into_string();
968 let val = CborValue::Text(encoded);
969
970 let result = value_to_bytes(&val).expect("should succeed");
971 assert_eq!(result, Some(original));
972 }
973
974 #[test]
975 fn value_to_bytes_from_invalid_base58_text_returns_none() {
976 let val = CborValue::Text("0OIl!!!".to_string());
978 let result = value_to_bytes(&val).expect("should succeed");
979 assert_eq!(result, None);
980 }
981
982 #[test]
983 fn value_to_bytes_from_integer_array() {
984 let val = CborValue::Array(vec![
985 CborValue::Integer(10.into()),
986 CborValue::Integer(20.into()),
987 CborValue::Integer(30.into()),
988 ]);
989
990 let result = value_to_bytes(&val).expect("should succeed");
991 assert_eq!(result, Some(vec![10, 20, 30]));
992 }
993
994 #[test]
995 fn value_to_bytes_from_array_with_non_integer_fails() {
996 let val = CborValue::Array(vec![
997 CborValue::Integer(1.into()),
998 CborValue::Text("not an int".to_string()),
999 ]);
1000
1001 let result = value_to_bytes(&val);
1002 assert!(result.is_err());
1003 }
1004
1005 #[test]
1006 fn value_to_bytes_from_bool_fails() {
1007 let val = CborValue::Bool(true);
1008 let result = value_to_bytes(&val);
1009 assert!(result.is_err());
1010 }
1011
1012 #[test]
1013 fn value_to_bytes_from_null_fails() {
1014 let val = CborValue::Null;
1015 let result = value_to_bytes(&val);
1016 assert!(result.is_err());
1017 }
1018
1019 #[test]
1022 fn value_to_hash_from_32_bytes() {
1023 let bytes = vec![42u8; 32];
1024 let val = CborValue::Bytes(bytes.clone());
1025
1026 let result = value_to_hash(&val).expect("should succeed");
1027 assert_eq!(result, [42u8; 32]);
1028 }
1029
1030 #[test]
1031 fn value_to_hash_from_wrong_length_bytes_fails() {
1032 let val = CborValue::Bytes(vec![1u8; 16]);
1033 let result = value_to_hash(&val);
1034 assert!(result.is_err());
1035 }
1036
1037 #[test]
1038 fn value_to_hash_from_valid_base58_text_32_bytes() {
1039 let original = [7u8; 32];
1040 let encoded = bs58::encode(&original).into_string();
1041 let val = CborValue::Text(encoded);
1042
1043 let result = value_to_hash(&val).expect("should succeed");
1044 assert_eq!(result, original);
1045 }
1046
1047 #[test]
1048 fn value_to_hash_from_invalid_base58_text_fails() {
1049 let val = CborValue::Text("!!!invalid!!!".to_string());
1050 let result = value_to_hash(&val);
1051 assert!(result.is_err());
1052 }
1053
1054 #[test]
1055 fn value_to_hash_from_base58_text_wrong_length_fails() {
1056 let encoded = bs58::encode(&[1u8; 4]).into_string();
1058 let val = CborValue::Text(encoded);
1059 let result = value_to_hash(&val);
1060 assert!(result.is_err());
1061 }
1062
1063 #[test]
1064 fn value_to_hash_from_integer_array_32_bytes() {
1065 let val = CborValue::Array((0..32).map(|i| CborValue::Integer(i.into())).collect());
1066
1067 let result = value_to_hash(&val).expect("should succeed");
1068 let expected: [u8; 32] = (0u8..32).collect::<Vec<u8>>().try_into().unwrap();
1069 assert_eq!(result, expected);
1070 }
1071
1072 #[test]
1073 fn value_to_hash_from_integer_array_wrong_length_fails() {
1074 let val = CborValue::Array(vec![CborValue::Integer(1.into()); 10]);
1075 let result = value_to_hash(&val);
1076 assert!(result.is_err());
1077 }
1078
1079 #[test]
1080 fn value_to_hash_from_array_with_non_integer_fails() {
1081 let mut arr: Vec<CborValue> = (0..31).map(|i| CborValue::Integer(i.into())).collect();
1082 arr.push(CborValue::Text("not_int".to_string()));
1083 let val = CborValue::Array(arr);
1084
1085 let result = value_to_hash(&val);
1086 assert!(result.is_err());
1087 }
1088
1089 #[test]
1090 fn value_to_hash_from_bool_fails() {
1091 let val = CborValue::Bool(false);
1092 let result = value_to_hash(&val);
1093 assert!(result.is_err());
1094 }
1095
1096 #[test]
1099 fn round_trip_canonical_map_through_bytes() {
1100 let mut map = CborCanonicalMap::new();
1101 map.insert("name", CborValue::Text("Alice".to_string()));
1102 map.insert("age", CborValue::Integer(30.into()));
1103 map.insert("active", CborValue::Bool(true));
1104
1105 let bytes = map.to_bytes().expect("should serialize");
1106
1107 let decoded: CborValue = ciborium::de::from_reader(&bytes[..]).expect("should deserialize");
1108 let decoded_map = CborCanonicalMap::try_from(decoded).expect("should convert to map");
1109
1110 assert_eq!(
1111 ValuesCollection::get(&decoded_map, &CborValue::Text("name".to_string())),
1112 Some(&CborValue::Text("Alice".to_string()))
1113 );
1114 assert_eq!(
1115 ValuesCollection::get(&decoded_map, &CborValue::Text("age".to_string())),
1116 Some(&CborValue::Integer(30.into()))
1117 );
1118 assert_eq!(
1119 ValuesCollection::get(&decoded_map, &CborValue::Text("active".to_string())),
1120 Some(&CborValue::Bool(true))
1121 );
1122 }
1123
1124 #[test]
1125 fn canonical_sort_with_same_length_keys_uses_lexicographic_order() {
1126 let mut map = CborCanonicalMap::new();
1127 map.insert("cc", CborValue::Integer(1.into()));
1128 map.insert("bb", CborValue::Integer(2.into()));
1129 map.insert("aa", CborValue::Integer(3.into()));
1130
1131 map.sort_canonical();
1132
1133 let value = map.to_value_unsorted();
1134 if let CborValue::Map(pairs) = value {
1135 let keys: Vec<&str> = pairs.iter().map(|(k, _)| k.as_text().unwrap()).collect();
1136 assert_eq!(keys, vec!["aa", "bb", "cc"]);
1137 }
1138 }
1139
1140 #[test]
1141 fn canonical_sort_shorter_keys_come_first() {
1142 let mut map = CborCanonicalMap::new();
1143 map.insert("zzz", CborValue::Integer(1.into()));
1144 map.insert("a", CborValue::Integer(2.into()));
1145 map.insert("bb", CborValue::Integer(3.into()));
1146
1147 map.sort_canonical();
1148
1149 let value = map.to_value_unsorted();
1150 if let CborValue::Map(pairs) = value {
1151 let keys: Vec<&str> = pairs.iter().map(|(k, _)| k.as_text().unwrap()).collect();
1152 assert_eq!(keys, vec!["a", "bb", "zzz"]);
1153 }
1154 }
1155}