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 let idx = step.as_integer()?;
213 let id: usize = idx.try_into().ok()?;
214 current_level = cbor_array.get_mut(id)?
215 }
216 _ => {
217 }
219 }
220 }
221 Some(current_level)
222 }
223}
224
225impl TryFrom<CborValue> for CborCanonicalMap {
226 type Error = ProtocolError;
227
228 fn try_from(value: CborValue) -> Result<Self, Self::Error> {
229 if let CborValue::Map(map) = value {
230 Ok(Self::from_vector(map))
231 } else {
232 Err(ProtocolError::ParsingError(
233 "Expected map to be a map".into(),
234 ))
235 }
236 }
237}
238
239impl From<Vec<(CborValue, CborValue)>> for CborCanonicalMap {
240 fn from(vec: Vec<(CborValue, CborValue)>) -> Self {
241 Self::from_vector(vec)
242 }
243}
244
245impl From<&Vec<(CborValue, CborValue)>> for CborCanonicalMap {
246 fn from(vec: &Vec<(CborValue, CborValue)>) -> Self {
247 Self::from_vector(vec.clone())
248 }
249}
250
251impl<T> From<&BTreeMap<String, T>> for CborCanonicalMap
252where
253 T: Into<CborValue> + Clone,
254{
255 fn from(map: &BTreeMap<String, T>) -> Self {
256 let vec = map
257 .iter()
258 .map(|(key, value)| (key.clone().into(), value.clone().into()))
259 .collect::<Vec<(CborValue, CborValue)>>();
260
261 Self::from(vec)
262 }
263}
264
265fn recursively_sort_canonical_cbor_map(cbor_map: &mut [(CborValue, CborValue)]) {
266 for (_, value) in cbor_map.iter_mut() {
267 if let CborValue::Map(map) = value {
268 recursively_sort_canonical_cbor_map(map)
269 }
270 if let CborValue::Array(array) = value {
271 for item in array.iter_mut() {
272 if let CborValue::Map(map) = item {
273 recursively_sort_canonical_cbor_map(map)
274 }
275 }
276 }
277 }
278
279 cbor_map.sort_by(|a, b| {
280 let key_a = a.0.as_text().unwrap().as_bytes();
283 let key_b = b.0.as_text().unwrap().as_bytes();
284
285 let len_comparison = key_a.len().cmp(&key_b.len());
286
287 match len_comparison {
288 Ordering::Less => Ordering::Less,
289 Ordering::Equal => key_a.cmp(key_b),
290 Ordering::Greater => Ordering::Greater,
291 }
292 });
293}
294
295pub fn value_to_bytes(value: &CborValue) -> Result<Option<Vec<u8>>, ProtocolError> {
297 match value {
298 CborValue::Bytes(bytes) => Ok(Some(bytes.clone())),
299 CborValue::Text(text) => match bs58::decode(text).into_vec() {
300 Ok(data) => Ok(Some(data)),
301 Err(_) => Ok(None),
302 },
303 CborValue::Array(array) => array
304 .iter()
305 .map(|byte| match byte {
306 CborValue::Integer(int) => {
307 let value_as_u8: u8 = (*int).try_into().map_err(|_| {
308 ProtocolError::DecodingError(String::from("expected u8 value"))
309 })?;
310 Ok(Some(value_as_u8))
311 }
312 _ => Err(ProtocolError::DecodingError(String::from(
313 "not an array of integers",
314 ))),
315 })
316 .collect::<Result<Option<Vec<u8>>, ProtocolError>>(),
317 _ => Err(ProtocolError::DecodingError(String::from(
318 "system value is incorrect type",
319 ))),
320 }
321}
322
323pub fn value_to_hash(value: &CborValue) -> Result<[u8; 32], ProtocolError> {
324 match value {
325 CborValue::Bytes(bytes) => bytes
326 .clone()
327 .try_into()
328 .map_err(|_| ProtocolError::DecodingError("expected 32 bytes".to_string())),
329 CborValue::Text(text) => match bs58::decode(text).into_vec() {
330 Ok(bytes) => bytes
331 .try_into()
332 .map_err(|_| ProtocolError::DecodingError("expected 32 bytes".to_string())),
333 Err(_) => Err(ProtocolError::DecodingError(
334 "expected 32 bytes".to_string(),
335 )),
336 },
337 CborValue::Array(array) => array
338 .iter()
339 .map(|byte| match byte {
340 CborValue::Integer(int) => {
341 let value_as_u8: u8 = (*int).try_into().map_err(|_| {
342 ProtocolError::DecodingError(String::from("expected u8 value"))
343 })?;
344 Ok(value_as_u8)
345 }
346 _ => Err(ProtocolError::DecodingError(String::from(
347 "not an array of integers",
348 ))),
349 })
350 .collect::<Result<Vec<u8>, ProtocolError>>()?
351 .try_into()
352 .map_err(|_| ProtocolError::DecodingError("expected 32 bytes".to_string())),
353 _ => Err(ProtocolError::DecodingError(String::from(
354 "system value is incorrect type",
355 ))),
356 }
357}
358
359#[cfg(test)]
360mod test {
361 use std::collections::BTreeMap;
362 use std::convert::TryFrom;
363 use std::convert::TryInto;
364
365 use crate::util::cbor_value::{ReplacePaths, ValuesCollection};
366
367 use super::{value_to_bytes, value_to_hash, CborCanonicalMap, CborValue, FieldType};
368 use ciborium::cbor;
369
370 #[test]
373 fn should_get_path_to_property_from_cbor() {
374 let cbor_value = cbor!( {
375 "alpha" => {
376 "bravo" => "bravo_value",
377 }
378 })
379 .expect("valid cbor");
380 let mut canonical: CborCanonicalMap = cbor_value.try_into().expect("valid canonical");
381 let result = canonical.get_path_mut("alpha.bravo").expect("bravo value");
382 assert_eq!(&mut CborValue::Text(String::from("bravo_value")), result);
383 }
384
385 #[test]
386 fn should_get_paths_to_array_from_cbor() {
387 let cbor_value = cbor!( {
388 "alpha" => {
389 "bravo" => ["bravo_first_item", "bravo_second_item" ],
390 }
391 })
392 .expect("valid cbor");
393 let mut canonical: CborCanonicalMap = cbor_value.try_into().expect("valid canonical");
394 let result = canonical
395 .get_path_mut("alpha.bravo[0]")
396 .expect("first item from bravo");
397 assert_eq!(
398 &mut CborValue::Text(String::from("bravo_first_item")),
399 result
400 );
401 }
402
403 #[test]
404 fn should_return_non_when_path_not_exist() {
405 let cbor_value = cbor!( {
406 "alpha" => {
407 "bravo" => ["bravo_first_item", "bravo_second_item" ],
408 }
409 })
410 .expect("valid cbor");
411 let mut canonical: CborCanonicalMap = cbor_value.try_into().expect("valid canonical");
412 let path = "alpha.bravo[-1]";
413
414 assert!(canonical.get_path_mut(path).is_none())
415 }
416
417 #[test]
418 fn should_replace_cbor_value() {
419 let cbor_value = cbor!({
420 "alpha" => {
421 "array_value" => vec![0_u8;32]
422
423 }
424 })
425 .expect("cbor should be created");
426
427 let mut canonical: CborCanonicalMap = cbor_value.try_into().expect("valid canonical");
428 canonical.replace_path(
429 "alpha.array_value",
430 FieldType::ArrayInt,
431 FieldType::StringBase58,
432 );
433
434 let replaced = canonical
435 .get_path_mut("alpha.array_value")
436 .expect("value should be returned");
437
438 assert_eq!(
439 &mut CborValue::Text(bs58::encode(vec![0_u8; 32]).into_string()),
440 replaced
441 );
442 }
443
444 #[test]
449 fn new_creates_empty_map() {
450 let map = CborCanonicalMap::new();
451 let value = map.to_value_unsorted();
452 assert_eq!(value, CborValue::Map(vec![]));
453 }
454
455 #[test]
456 fn default_creates_empty_map() {
457 let map = CborCanonicalMap::default();
458 let value = map.to_value_unsorted();
459 assert_eq!(value, CborValue::Map(vec![]));
460 }
461
462 #[test]
463 fn insert_adds_key_value_pair() {
464 let mut map = CborCanonicalMap::new();
465 map.insert("hello", CborValue::Text("world".to_string()));
466
467 let val = ValuesCollection::get(&map, &CborValue::Text("hello".to_string()));
468 assert_eq!(val, Some(&CborValue::Text("world".to_string())));
469 }
470
471 #[test]
472 fn insert_multiple_keys() {
473 let mut map = CborCanonicalMap::new();
474 map.insert("a", CborValue::Integer(1.into()));
475 map.insert("b", CborValue::Integer(2.into()));
476 map.insert("c", CborValue::Integer(3.into()));
477
478 assert_eq!(
479 ValuesCollection::get(&map, &CborValue::Text("a".to_string())),
480 Some(&CborValue::Integer(1.into()))
481 );
482 assert_eq!(
483 ValuesCollection::get(&map, &CborValue::Text("b".to_string())),
484 Some(&CborValue::Integer(2.into()))
485 );
486 assert_eq!(
487 ValuesCollection::get(&map, &CborValue::Text("c".to_string())),
488 Some(&CborValue::Integer(3.into()))
489 );
490 }
491
492 #[test]
493 fn remove_existing_key() {
494 let mut map = CborCanonicalMap::new();
495 map.insert("key1", CborValue::Bool(true));
496 map.insert("key2", CborValue::Bool(false));
497
498 map.remove("key1");
499
500 assert!(ValuesCollection::get(&map, &CborValue::Text("key1".to_string())).is_none());
501 assert_eq!(
502 ValuesCollection::get(&map, &CborValue::Text("key2".to_string())),
503 Some(&CborValue::Bool(false))
504 );
505 }
506
507 #[test]
508 fn remove_nonexistent_key_is_noop() {
509 let mut map = CborCanonicalMap::new();
510 map.insert("key1", CborValue::Bool(true));
511
512 map.remove("nonexistent");
514
515 assert_eq!(
516 ValuesCollection::get(&map, &CborValue::Text("key1".to_string())),
517 Some(&CborValue::Bool(true))
518 );
519 }
520
521 #[test]
522 fn get_mut_returns_mutable_reference() {
523 let mut map = CborCanonicalMap::new();
524 map.insert("key", CborValue::Integer(10.into()));
525
526 let val = map.get_mut(&CborValue::Text("key".to_string()));
527 assert!(val.is_some());
528 *val.unwrap() = CborValue::Integer(20.into());
529
530 assert_eq!(
531 ValuesCollection::get(&map, &CborValue::Text("key".to_string())),
532 Some(&CborValue::Integer(20.into()))
533 );
534 }
535
536 #[test]
537 fn get_mut_returns_none_for_missing_key() {
538 let mut map = CborCanonicalMap::new();
539 assert!(map
540 .get_mut(&CborValue::Text("missing".to_string()))
541 .is_none());
542 }
543
544 #[test]
545 fn set_replaces_existing_value() {
546 let mut map = CborCanonicalMap::new();
547 map.insert("key", CborValue::Integer(1.into()));
548
549 let result = map.set(
550 &CborValue::Text("key".to_string()),
551 CborValue::Integer(99.into()),
552 );
553 assert!(result.is_some());
554
555 assert_eq!(
556 ValuesCollection::get(&map, &CborValue::Text("key".to_string())),
557 Some(&CborValue::Integer(99.into()))
558 );
559 }
560
561 #[test]
562 fn set_returns_none_for_missing_key() {
563 let mut map = CborCanonicalMap::new();
564
565 let result = map.set(
566 &CborValue::Text("missing".to_string()),
567 CborValue::Integer(1.into()),
568 );
569 assert!(result.is_none());
570 }
571
572 #[test]
573 fn from_vector_creates_map() {
574 let vec = vec![
575 (
576 CborValue::Text("a".to_string()),
577 CborValue::Integer(1.into()),
578 ),
579 (
580 CborValue::Text("b".to_string()),
581 CborValue::Integer(2.into()),
582 ),
583 ];
584
585 let map = CborCanonicalMap::from_vector(vec);
586
587 assert_eq!(
588 ValuesCollection::get(&map, &CborValue::Text("a".to_string())),
589 Some(&CborValue::Integer(1.into()))
590 );
591 assert_eq!(
592 ValuesCollection::get(&map, &CborValue::Text("b".to_string())),
593 Some(&CborValue::Integer(2.into()))
594 );
595 }
596
597 #[test]
598 fn from_serializable_with_btreemap() {
599 let mut btree = BTreeMap::new();
600 btree.insert("name".to_string(), "test".to_string());
601
602 let map =
603 CborCanonicalMap::from_serializable(&btree).expect("should serialize from BTreeMap");
604
605 assert!(ValuesCollection::get(&map, &CborValue::Text("name".to_string())).is_some());
606 }
607
608 #[test]
609 fn from_serializable_with_non_map_value_fails() {
610 let result = CborCanonicalMap::from_serializable(&"just a string");
612 assert!(result.is_err());
613 }
614
615 #[test]
618 fn sort_canonical_orders_by_key_length_then_lexicographic() {
619 let mut map = CborCanonicalMap::new();
620 map.insert("beta", CborValue::Integer(2.into()));
622 map.insert("a", CborValue::Integer(1.into()));
623 map.insert("cc", CborValue::Integer(3.into()));
624 map.insert("bb", CborValue::Integer(4.into()));
625
626 map.sort_canonical();
627
628 let sorted = map.to_value_unsorted();
629 if let CborValue::Map(pairs) = sorted {
630 let keys: Vec<&str> = pairs.iter().map(|(k, _)| k.as_text().unwrap()).collect();
631 assert_eq!(keys, vec!["a", "bb", "cc", "beta"]);
633 } else {
634 panic!("Expected map");
635 }
636 }
637
638 #[test]
639 fn sort_canonical_recursively_sorts_nested_maps() {
640 let mut map = CborCanonicalMap::new();
641 let nested = CborValue::Map(vec![
643 (
644 CborValue::Text("zz".to_string()),
645 CborValue::Integer(1.into()),
646 ),
647 (
648 CborValue::Text("a".to_string()),
649 CborValue::Integer(2.into()),
650 ),
651 ]);
652 map.insert("outer", nested);
653
654 map.sort_canonical();
655
656 let sorted = map.to_value_unsorted();
657 if let CborValue::Map(pairs) = sorted {
658 if let CborValue::Map(inner_pairs) = &pairs[0].1 {
659 let keys: Vec<&str> = inner_pairs
660 .iter()
661 .map(|(k, _)| k.as_text().unwrap())
662 .collect();
663 assert_eq!(keys, vec!["a", "zz"]);
665 } else {
666 panic!("Expected nested map");
667 }
668 }
669 }
670
671 #[test]
672 fn sort_canonical_recursively_sorts_maps_inside_arrays() {
673 let mut map = CborCanonicalMap::new();
674 let nested_map_in_array = CborValue::Array(vec![CborValue::Map(vec![
675 (
676 CborValue::Text("zz".to_string()),
677 CborValue::Integer(1.into()),
678 ),
679 (
680 CborValue::Text("a".to_string()),
681 CborValue::Integer(2.into()),
682 ),
683 ])]);
684 map.insert("items", nested_map_in_array);
685
686 map.sort_canonical();
687
688 let sorted = map.to_value_unsorted();
689 if let CborValue::Map(pairs) = sorted {
690 if let CborValue::Array(arr) = &pairs[0].1 {
691 if let CborValue::Map(inner_pairs) = &arr[0] {
692 let keys: Vec<&str> = inner_pairs
693 .iter()
694 .map(|(k, _)| k.as_text().unwrap())
695 .collect();
696 assert_eq!(keys, vec!["a", "zz"]);
697 } else {
698 panic!("Expected map inside array");
699 }
700 } else {
701 panic!("Expected array");
702 }
703 }
704 }
705
706 #[test]
709 fn to_bytes_produces_valid_cbor() {
710 let mut map = CborCanonicalMap::new();
711 map.insert("key", CborValue::Text("value".to_string()));
712
713 let bytes = map.to_bytes().expect("should serialize to bytes");
714 assert!(!bytes.is_empty());
715
716 let deserialized: CborValue =
718 ciborium::de::from_reader(&bytes[..]).expect("should deserialize");
719 if let CborValue::Map(pairs) = deserialized {
720 assert_eq!(pairs.len(), 1);
721 assert_eq!(
722 pairs[0],
723 (
724 CborValue::Text("key".to_string()),
725 CborValue::Text("value".to_string())
726 )
727 );
728 } else {
729 panic!("Expected map after deserialization");
730 }
731 }
732
733 #[test]
734 fn to_bytes_sorts_before_serializing() {
735 let mut map = CborCanonicalMap::new();
736 map.insert("beta", CborValue::Integer(2.into()));
737 map.insert("a", CborValue::Integer(1.into()));
738
739 let bytes = map.to_bytes().expect("should serialize");
740 let deserialized: CborValue =
741 ciborium::de::from_reader(&bytes[..]).expect("should deserialize");
742
743 if let CborValue::Map(pairs) = deserialized {
744 let keys: Vec<&str> = pairs.iter().map(|(k, _)| k.as_text().unwrap()).collect();
745 assert_eq!(keys, vec!["a", "beta"]);
746 }
747 }
748
749 #[test]
750 fn to_value_unsorted_preserves_insertion_order() {
751 let mut map = CborCanonicalMap::new();
752 map.insert("z", CborValue::Integer(1.into()));
753 map.insert("a", CborValue::Integer(2.into()));
754
755 let value = map.to_value_unsorted();
756 if let CborValue::Map(pairs) = value {
757 assert_eq!(pairs[0].0, CborValue::Text("z".to_string()));
758 assert_eq!(pairs[1].0, CborValue::Text("a".to_string()));
759 }
760 }
761
762 #[test]
763 fn to_value_sorted_returns_sorted_map() {
764 let mut map = CborCanonicalMap::new();
765 map.insert("beta", CborValue::Integer(2.into()));
766 map.insert("a", CborValue::Integer(1.into()));
767
768 let value = map.to_value_sorted();
769 if let CborValue::Map(pairs) = value {
770 assert_eq!(pairs[0].0, CborValue::Text("a".to_string()));
771 assert_eq!(pairs[1].0, CborValue::Text("beta".to_string()));
772 }
773 }
774
775 #[test]
776 fn to_value_clone_returns_sorted_clone() {
777 let mut map = CborCanonicalMap::new();
778 map.insert("beta", CborValue::Integer(2.into()));
779 map.insert("a", CborValue::Integer(1.into()));
780
781 let value = map.to_value_clone();
782 if let CborValue::Map(pairs) = value {
783 assert_eq!(pairs[0].0, CborValue::Text("a".to_string()));
784 assert_eq!(pairs[1].0, CborValue::Text("beta".to_string()));
785 }
786
787 let val = ValuesCollection::get(&map, &CborValue::Text("a".to_string()));
789 assert!(val.is_some());
790 }
791
792 #[test]
795 fn try_from_cbor_map_succeeds() {
796 let cbor = CborValue::Map(vec![(
797 CborValue::Text("k".to_string()),
798 CborValue::Bool(true),
799 )]);
800
801 let map = CborCanonicalMap::try_from(cbor).expect("should convert from map");
802 assert_eq!(
803 ValuesCollection::get(&map, &CborValue::Text("k".to_string())),
804 Some(&CborValue::Bool(true))
805 );
806 }
807
808 #[test]
809 fn try_from_non_map_fails() {
810 let cbor = CborValue::Text("not a map".to_string());
811 let result = CborCanonicalMap::try_from(cbor);
812 assert!(result.is_err());
813 }
814
815 #[test]
816 fn from_vec_creates_canonical_map() {
817 let vec = vec![(
818 CborValue::Text("x".to_string()),
819 CborValue::Integer(42.into()),
820 )];
821
822 let map: CborCanonicalMap = vec.into();
823 assert_eq!(
824 ValuesCollection::get(&map, &CborValue::Text("x".to_string())),
825 Some(&CborValue::Integer(42.into()))
826 );
827 }
828
829 #[test]
830 fn from_ref_vec_creates_canonical_map() {
831 let vec = vec![(
832 CborValue::Text("y".to_string()),
833 CborValue::Integer(7.into()),
834 )];
835
836 let map: CborCanonicalMap = (&vec).into();
837 assert_eq!(
838 ValuesCollection::get(&map, &CborValue::Text("y".to_string())),
839 Some(&CborValue::Integer(7.into()))
840 );
841 }
842
843 #[test]
844 fn from_btreemap_string_creates_canonical_map() {
845 let mut btree = BTreeMap::new();
846 btree.insert("alpha".to_string(), CborValue::Integer(1.into()));
847 btree.insert("beta".to_string(), CborValue::Integer(2.into()));
848
849 let map: CborCanonicalMap = (&btree).into();
850 assert_eq!(
851 ValuesCollection::get(&map, &CborValue::Text("alpha".to_string())),
852 Some(&CborValue::Integer(1.into()))
853 );
854 assert_eq!(
855 ValuesCollection::get(&map, &CborValue::Text("beta".to_string())),
856 Some(&CborValue::Integer(2.into()))
857 );
858 }
859
860 #[test]
863 fn values_collection_get_returns_value() {
864 let map = CborCanonicalMap::from_vector(vec![(
865 CborValue::Text("k".to_string()),
866 CborValue::Text("v".to_string()),
867 )]);
868
869 let result = ValuesCollection::get(&map, &CborValue::Text("k".to_string()));
870 assert_eq!(result, Some(&CborValue::Text("v".to_string())));
871 }
872
873 #[test]
874 fn values_collection_get_returns_none_for_missing() {
875 let map = CborCanonicalMap::new();
876 let result = ValuesCollection::get(&map, &CborValue::Text("missing".to_string()));
877 assert!(result.is_none());
878 }
879
880 #[test]
881 fn values_collection_remove_returns_removed_value() {
882 let mut map = CborCanonicalMap::from_vector(vec![
883 (
884 CborValue::Text("a".to_string()),
885 CborValue::Integer(1.into()),
886 ),
887 (
888 CborValue::Text("b".to_string()),
889 CborValue::Integer(2.into()),
890 ),
891 ]);
892
893 let removed = ValuesCollection::remove(&mut map, "a");
894 assert_eq!(removed, Some(CborValue::Integer(1.into())));
895 assert!(ValuesCollection::get(&map, &CborValue::Text("a".to_string())).is_none());
896 }
897
898 #[test]
899 fn values_collection_remove_returns_none_for_missing() {
900 let mut map = CborCanonicalMap::new();
901 let removed = ValuesCollection::remove(&mut map, "nonexistent");
902 assert!(removed.is_none());
903 }
904
905 #[test]
908 fn replace_paths_converts_multiple_paths() {
909 let cbor_value = cbor!({
910 "field1" => vec![0_u8; 32],
911 "field2" => vec![1_u8; 32]
912 })
913 .expect("valid cbor");
914
915 let mut canonical: CborCanonicalMap = cbor_value.try_into().expect("valid canonical");
916 ReplacePaths::replace_paths(
917 &mut canonical,
918 vec!["field1", "field2"],
919 FieldType::ArrayInt,
920 FieldType::Bytes,
921 );
922
923 let v1 = ValuesCollection::get(&canonical, &CborValue::Text("field1".to_string()));
924 assert!(matches!(v1, Some(CborValue::Bytes(_))));
925 let v2 = ValuesCollection::get(&canonical, &CborValue::Text("field2".to_string()));
926 assert!(matches!(v2, Some(CborValue::Bytes(_))));
927 }
928
929 #[test]
930 fn replace_path_returns_none_for_nonexistent_path() {
931 let mut map = CborCanonicalMap::new();
932 map.insert("exists", CborValue::Text("value".to_string()));
933
934 let result =
935 ReplacePaths::replace_path(&mut map, "nonexistent", FieldType::Bytes, FieldType::Bytes);
936 assert!(result.is_none());
937 }
938
939 #[test]
940 fn get_path_mut_with_empty_path_returns_none() {
941 let mut map = CborCanonicalMap::new();
942 map.insert("key", CborValue::Integer(1.into()));
943
944 let result = ReplacePaths::get_path_mut(&mut map, "");
947 assert!(result.is_none());
950 }
951
952 #[test]
955 fn value_to_bytes_from_bytes() {
956 let val = CborValue::Bytes(vec![1, 2, 3, 4]);
957 let result = value_to_bytes(&val).expect("should succeed");
958 assert_eq!(result, Some(vec![1, 2, 3, 4]));
959 }
960
961 #[test]
962 fn value_to_bytes_from_valid_base58_text() {
963 let original = vec![1, 2, 3, 4, 5];
964 let encoded = bs58::encode(&original).into_string();
965 let val = CborValue::Text(encoded);
966
967 let result = value_to_bytes(&val).expect("should succeed");
968 assert_eq!(result, Some(original));
969 }
970
971 #[test]
972 fn value_to_bytes_from_invalid_base58_text_returns_none() {
973 let val = CborValue::Text("0OIl!!!".to_string());
975 let result = value_to_bytes(&val).expect("should succeed");
976 assert_eq!(result, None);
977 }
978
979 #[test]
980 fn value_to_bytes_from_integer_array() {
981 let val = CborValue::Array(vec![
982 CborValue::Integer(10.into()),
983 CborValue::Integer(20.into()),
984 CborValue::Integer(30.into()),
985 ]);
986
987 let result = value_to_bytes(&val).expect("should succeed");
988 assert_eq!(result, Some(vec![10, 20, 30]));
989 }
990
991 #[test]
992 fn value_to_bytes_from_array_with_non_integer_fails() {
993 let val = CborValue::Array(vec![
994 CborValue::Integer(1.into()),
995 CborValue::Text("not an int".to_string()),
996 ]);
997
998 let result = value_to_bytes(&val);
999 assert!(result.is_err());
1000 }
1001
1002 #[test]
1003 fn value_to_bytes_from_bool_fails() {
1004 let val = CborValue::Bool(true);
1005 let result = value_to_bytes(&val);
1006 assert!(result.is_err());
1007 }
1008
1009 #[test]
1010 fn value_to_bytes_from_null_fails() {
1011 let val = CborValue::Null;
1012 let result = value_to_bytes(&val);
1013 assert!(result.is_err());
1014 }
1015
1016 #[test]
1019 fn value_to_hash_from_32_bytes() {
1020 let bytes = vec![42u8; 32];
1021 let val = CborValue::Bytes(bytes.clone());
1022
1023 let result = value_to_hash(&val).expect("should succeed");
1024 assert_eq!(result, [42u8; 32]);
1025 }
1026
1027 #[test]
1028 fn value_to_hash_from_wrong_length_bytes_fails() {
1029 let val = CborValue::Bytes(vec![1u8; 16]);
1030 let result = value_to_hash(&val);
1031 assert!(result.is_err());
1032 }
1033
1034 #[test]
1035 fn value_to_hash_from_valid_base58_text_32_bytes() {
1036 let original = [7u8; 32];
1037 let encoded = bs58::encode(&original).into_string();
1038 let val = CborValue::Text(encoded);
1039
1040 let result = value_to_hash(&val).expect("should succeed");
1041 assert_eq!(result, original);
1042 }
1043
1044 #[test]
1045 fn value_to_hash_from_invalid_base58_text_fails() {
1046 let val = CborValue::Text("!!!invalid!!!".to_string());
1047 let result = value_to_hash(&val);
1048 assert!(result.is_err());
1049 }
1050
1051 #[test]
1052 fn value_to_hash_from_base58_text_wrong_length_fails() {
1053 let encoded = bs58::encode(&[1u8; 4]).into_string();
1055 let val = CborValue::Text(encoded);
1056 let result = value_to_hash(&val);
1057 assert!(result.is_err());
1058 }
1059
1060 #[test]
1061 fn value_to_hash_from_integer_array_32_bytes() {
1062 let val = CborValue::Array((0..32).map(|i| CborValue::Integer(i.into())).collect());
1063
1064 let result = value_to_hash(&val).expect("should succeed");
1065 let expected: [u8; 32] = (0u8..32).collect::<Vec<u8>>().try_into().unwrap();
1066 assert_eq!(result, expected);
1067 }
1068
1069 #[test]
1070 fn value_to_hash_from_integer_array_wrong_length_fails() {
1071 let val = CborValue::Array(vec![CborValue::Integer(1.into()); 10]);
1072 let result = value_to_hash(&val);
1073 assert!(result.is_err());
1074 }
1075
1076 #[test]
1077 fn value_to_hash_from_array_with_non_integer_fails() {
1078 let mut arr: Vec<CborValue> = (0..31).map(|i| CborValue::Integer(i.into())).collect();
1079 arr.push(CborValue::Text("not_int".to_string()));
1080 let val = CborValue::Array(arr);
1081
1082 let result = value_to_hash(&val);
1083 assert!(result.is_err());
1084 }
1085
1086 #[test]
1087 fn value_to_hash_from_bool_fails() {
1088 let val = CborValue::Bool(false);
1089 let result = value_to_hash(&val);
1090 assert!(result.is_err());
1091 }
1092
1093 #[test]
1096 fn round_trip_canonical_map_through_bytes() {
1097 let mut map = CborCanonicalMap::new();
1098 map.insert("name", CborValue::Text("Alice".to_string()));
1099 map.insert("age", CborValue::Integer(30.into()));
1100 map.insert("active", CborValue::Bool(true));
1101
1102 let bytes = map.to_bytes().expect("should serialize");
1103
1104 let decoded: CborValue = ciborium::de::from_reader(&bytes[..]).expect("should deserialize");
1105 let decoded_map = CborCanonicalMap::try_from(decoded).expect("should convert to map");
1106
1107 assert_eq!(
1108 ValuesCollection::get(&decoded_map, &CborValue::Text("name".to_string())),
1109 Some(&CborValue::Text("Alice".to_string()))
1110 );
1111 assert_eq!(
1112 ValuesCollection::get(&decoded_map, &CborValue::Text("age".to_string())),
1113 Some(&CborValue::Integer(30.into()))
1114 );
1115 assert_eq!(
1116 ValuesCollection::get(&decoded_map, &CborValue::Text("active".to_string())),
1117 Some(&CborValue::Bool(true))
1118 );
1119 }
1120
1121 #[test]
1122 fn canonical_sort_with_same_length_keys_uses_lexicographic_order() {
1123 let mut map = CborCanonicalMap::new();
1124 map.insert("cc", CborValue::Integer(1.into()));
1125 map.insert("bb", CborValue::Integer(2.into()));
1126 map.insert("aa", CborValue::Integer(3.into()));
1127
1128 map.sort_canonical();
1129
1130 let value = map.to_value_unsorted();
1131 if let CborValue::Map(pairs) = value {
1132 let keys: Vec<&str> = pairs.iter().map(|(k, _)| k.as_text().unwrap()).collect();
1133 assert_eq!(keys, vec!["aa", "bb", "cc"]);
1134 }
1135 }
1136
1137 #[test]
1138 fn canonical_sort_shorter_keys_come_first() {
1139 let mut map = CborCanonicalMap::new();
1140 map.insert("zzz", CborValue::Integer(1.into()));
1141 map.insert("a", CborValue::Integer(2.into()));
1142 map.insert("bb", CborValue::Integer(3.into()));
1143
1144 map.sort_canonical();
1145
1146 let value = map.to_value_unsorted();
1147 if let CborValue::Map(pairs) = value {
1148 let keys: Vec<&str> = pairs.iter().map(|(k, _)| k.as_text().unwrap()).collect();
1149 assert_eq!(keys, vec!["a", "bb", "zzz"]);
1150 }
1151 }
1152}