1use crate::inner_value_at_path::is_array_path;
2use crate::value_map::ValueMapHelper;
3use crate::{Error, Value};
4use std::collections::BTreeMap;
5
6use base64::prelude::BASE64_STANDARD;
7use base64::Engine;
8use std::iter::Peekable;
9use std::vec::IntoIter;
10
11#[derive(Debug, Clone, Copy)]
12pub enum IntegerReplacementType {
13 U128,
14 I128,
15 U64,
16 I64,
17 U32,
18 I32,
19 U16,
20 I16,
21 U8,
22 I8,
23}
24
25impl IntegerReplacementType {
26 pub fn replace_for_value(&self, value: Value) -> Result<Value, Error> {
27 Ok(match self {
28 IntegerReplacementType::U128 => Value::U128(value.try_into()?),
29 IntegerReplacementType::I128 => Value::I128(value.try_into()?),
30 IntegerReplacementType::U64 => Value::U64(value.try_into()?),
31 IntegerReplacementType::I64 => Value::I64(value.try_into()?),
32 IntegerReplacementType::U32 => Value::U32(value.try_into()?),
33 IntegerReplacementType::I32 => Value::I32(value.try_into()?),
34 IntegerReplacementType::U16 => Value::U16(value.try_into()?),
35 IntegerReplacementType::I16 => Value::I16(value.try_into()?),
36 IntegerReplacementType::U8 => Value::U8(value.try_into()?),
37 IntegerReplacementType::I8 => Value::I8(value.try_into()?),
38 })
39 }
40}
41
42#[derive(Debug, Clone, Copy)]
43pub enum ReplacementType {
44 Identifier,
45 BinaryBytes,
46 TextBase58,
47 TextBase64,
48}
49
50impl ReplacementType {
51 pub fn replace_for_bytes(&self, bytes: Vec<u8>) -> Result<Value, Error> {
52 match self {
53 ReplacementType::Identifier => {
54 Ok(Value::Identifier(bytes.try_into().map_err(|_| {
55 Error::ByteLengthNot32BytesError(String::from(
56 "Trying to replace into an identifier, but not 32 bytes long",
57 ))
58 })?))
59 }
60 ReplacementType::BinaryBytes => Ok(Value::Bytes(bytes)),
61 ReplacementType::TextBase58 => Ok(Value::Text(bs58::encode(bytes).into_string())),
62 ReplacementType::TextBase64 => Ok(Value::Text(BASE64_STANDARD.encode(bytes))),
63 }
64 }
65
66 pub fn replace_for_bytes_20(&self, bytes: [u8; 20]) -> Result<Value, Error> {
67 match self {
68 ReplacementType::BinaryBytes => Ok(Value::Bytes20(bytes)),
69 ReplacementType::TextBase58 => Ok(Value::Text(bs58::encode(bytes).into_string())),
70 ReplacementType::TextBase64 => Ok(Value::Text(BASE64_STANDARD.encode(bytes))),
71 _ => Err(Error::ByteLengthNot36BytesError(
72 "trying to replace 36 bytes into an identifier".to_string(),
73 )),
74 }
75 }
76
77 pub fn replace_for_bytes_32(&self, bytes: [u8; 32]) -> Result<Value, Error> {
78 match self {
79 ReplacementType::Identifier => Ok(Value::Identifier(bytes)),
80 ReplacementType::BinaryBytes => Ok(Value::Bytes32(bytes)),
81 ReplacementType::TextBase58 => Ok(Value::Text(bs58::encode(bytes).into_string())),
82 ReplacementType::TextBase64 => Ok(Value::Text(BASE64_STANDARD.encode(bytes))),
83 }
84 }
85
86 pub fn replace_for_bytes_36(&self, bytes: [u8; 36]) -> Result<Value, Error> {
87 match self {
88 ReplacementType::BinaryBytes => Ok(Value::Bytes36(bytes)),
89 ReplacementType::TextBase58 => Ok(Value::Text(bs58::encode(bytes).into_string())),
90 ReplacementType::TextBase64 => Ok(Value::Text(BASE64_STANDARD.encode(bytes))),
91 _ => Err(Error::ByteLengthNot36BytesError(
92 "trying to replace 36 bytes into an identifier".to_string(),
93 )),
94 }
95 }
96
97 pub fn replace_consume_value(&self, value: Value) -> Result<Value, Error> {
98 let bytes = value.into_identifier_bytes()?;
99 self.replace_for_bytes(bytes)
100 }
101
102 pub fn replace_value_in_place(&self, value: &mut Value) -> Result<(), Error> {
103 let bytes = value.take().into_identifier_bytes()?;
104 *value = self.replace_for_bytes(bytes)?;
105 Ok(())
106 }
107}
108
109pub trait BTreeValueMapReplacementPathHelper {
110 fn replace_at_path(
111 &mut self,
112 path: &str,
113 replacement_type: ReplacementType,
114 ) -> Result<(), Error>;
115 fn replace_at_paths<'a, I: IntoIterator<Item = &'a String>>(
116 &mut self,
117 paths: I,
118 replacement_type: ReplacementType,
119 ) -> Result<(), Error>;
120}
121
122pub(crate) fn replace_leaf(
128 value: &mut Value,
129 replacement_type: ReplacementType,
130) -> Result<(), Error> {
131 match value {
132 Value::Bytes20(bytes) => {
133 *value = replacement_type.replace_for_bytes_20(*bytes)?;
134 }
135 Value::Bytes32(bytes) => {
136 *value = replacement_type.replace_for_bytes_32(*bytes)?;
137 }
138 Value::Bytes36(bytes) => {
139 *value = replacement_type.replace_for_bytes_36(*bytes)?;
140 }
141 _ => {
142 let bytes = match replacement_type {
143 ReplacementType::Identifier | ReplacementType::TextBase58 => {
144 value.to_identifier_bytes()
145 }
146 ReplacementType::BinaryBytes | ReplacementType::TextBase64 => {
147 value.to_binary_bytes()
148 }
149 }?;
150 *value = replacement_type.replace_for_bytes(bytes)?;
151 }
152 }
153 Ok(())
154}
155
156fn list_members(list: &mut Value, index: Option<usize>) -> Result<Vec<&mut Value>, Error> {
159 let list = list.to_array_mut()?;
160 match index {
161 Some(index) => match list.get_mut(index) {
162 Some(member) => Ok(vec![member]),
163 None => Err(Error::StructureError(format!(
164 "element at position {index} in array does not exist"
165 ))),
166 },
167 None => Ok(list.iter_mut().collect()),
168 }
169}
170
171fn replace_down(
172 mut current_values: Vec<&mut Value>,
173 mut split: Peekable<IntoIter<&str>>,
174 replacement_type: ReplacementType,
175) -> Result<(), Error> {
176 if let Some(path_component) = split.next() {
177 let is_last_component = split.peek().is_none();
178 let next_values = current_values
179 .iter_mut()
180 .map(|current_value| {
181 if current_value.is_map() {
182 let map = current_value.as_map_mut_ref()?;
183 if let Some((list_name, index)) = is_array_path(path_component)? {
187 let Some(list) = map.get_optional_key_mut(list_name) else {
188 return Ok(None);
189 };
190 let members = list_members(list, index)?;
191 if is_last_component {
192 for member in members {
193 replace_leaf(member, replacement_type)?;
194 }
195 return Ok(None);
196 }
197 return Ok(Some(members));
198 }
199 let Some(new_value) = map.get_optional_key_mut(path_component) else {
200 return Ok(None);
201 };
202 if is_last_component {
203 replace_leaf(new_value, replacement_type)?;
204 Ok(None)
205 } else {
206 Ok(Some(vec![new_value]))
207 }
208 } else if current_value.is_array() {
209 let array = current_value.to_array_mut()?.iter_mut().collect();
211 Ok(Some(array))
212 } else {
213 Err(Error::PathError("path was not an array or map".to_string()))
214 }
215 })
216 .collect::<Result<Vec<_>, Error>>()?
217 .into_iter()
218 .flatten()
219 .flatten()
220 .collect();
221 replace_down(next_values, split, replacement_type)
222 } else {
223 Ok(())
224 }
225}
226
227impl BTreeValueMapReplacementPathHelper for BTreeMap<String, Value> {
228 fn replace_at_path(
229 &mut self,
230 path: &str,
231 replacement_type: ReplacementType,
232 ) -> Result<(), Error> {
233 let mut split: Vec<_> = path.split('.').collect();
234 let first = split.first();
235 let Some(first_path_component) = first else {
236 return Err(Error::PathError("path was empty".to_string()));
237 };
238 if let Some((list_name, index)) = is_array_path(first_path_component)? {
240 let Some(list) = self.get_mut(list_name) else {
241 return Ok(());
242 };
243 let members = list_members(list, index)?;
244 if split.len() == 1 {
245 for member in members {
246 replace_leaf(member, replacement_type)?;
247 }
248 return Ok(());
249 }
250 split.remove(0);
251 return replace_down(members, split.into_iter().peekable(), replacement_type);
252 }
253 let Some(current_value) = self.get_mut(first_path_component.to_owned()) else {
254 return Ok(());
255 };
256 if split.len() == 1 {
257 replace_leaf(current_value, replacement_type)
258 } else {
259 split.remove(0);
260 let current_values = vec![current_value];
261 replace_down(
263 current_values,
264 split.into_iter().peekable(),
265 replacement_type,
266 )
267 }
268 }
269
270 fn replace_at_paths<'a, I: IntoIterator<Item = &'a String>>(
271 &mut self,
272 paths: I,
273 replacement_type: ReplacementType,
274 ) -> Result<(), Error> {
275 paths
276 .into_iter()
277 .try_for_each(|path| self.replace_at_path(path.as_str(), replacement_type))
278 }
279}
280
281#[cfg(test)]
282mod tests {
283 use super::*;
284 use crate::string_encoding::Encoding;
285 use crate::value_map::ValueMapHelper;
286 use crate::{Error, Identifier, Value};
287 use base64::prelude::BASE64_STANDARD;
288 use base64::Engine;
289 use std::collections::BTreeMap;
290
291 #[test]
296 fn integer_replacement_u8() {
297 let result = IntegerReplacementType::U8
298 .replace_for_value(Value::U64(200))
299 .unwrap();
300 assert_eq!(result, Value::U8(200));
301 }
302
303 #[test]
304 fn integer_replacement_i8() {
305 let result = IntegerReplacementType::I8
306 .replace_for_value(Value::I64(-100))
307 .unwrap();
308 assert_eq!(result, Value::I8(-100));
309 }
310
311 #[test]
312 fn integer_replacement_u16() {
313 let result = IntegerReplacementType::U16
314 .replace_for_value(Value::U64(60000))
315 .unwrap();
316 assert_eq!(result, Value::U16(60000));
317 }
318
319 #[test]
320 fn integer_replacement_i16() {
321 let result = IntegerReplacementType::I16
322 .replace_for_value(Value::I64(-30000))
323 .unwrap();
324 assert_eq!(result, Value::I16(-30000));
325 }
326
327 #[test]
328 fn integer_replacement_u32() {
329 let result = IntegerReplacementType::U32
330 .replace_for_value(Value::U64(3_000_000))
331 .unwrap();
332 assert_eq!(result, Value::U32(3_000_000));
333 }
334
335 #[test]
336 fn integer_replacement_i32() {
337 let result = IntegerReplacementType::I32
338 .replace_for_value(Value::I64(-3_000_000))
339 .unwrap();
340 assert_eq!(result, Value::I32(-3_000_000));
341 }
342
343 #[test]
344 fn integer_replacement_u64() {
345 let result = IntegerReplacementType::U64
346 .replace_for_value(Value::U64(u64::MAX))
347 .unwrap();
348 assert_eq!(result, Value::U64(u64::MAX));
349 }
350
351 #[test]
352 fn integer_replacement_i64() {
353 let result = IntegerReplacementType::I64
354 .replace_for_value(Value::I64(i64::MIN))
355 .unwrap();
356 assert_eq!(result, Value::I64(i64::MIN));
357 }
358
359 #[test]
360 fn integer_replacement_u128() {
361 let result = IntegerReplacementType::U128
362 .replace_for_value(Value::U64(42))
363 .unwrap();
364 assert_eq!(result, Value::U128(42));
365 }
366
367 #[test]
368 fn integer_replacement_i128() {
369 let result = IntegerReplacementType::I128
370 .replace_for_value(Value::I64(-42))
371 .unwrap();
372 assert_eq!(result, Value::I128(-42));
373 }
374
375 #[test]
376 fn integer_replacement_overflow_error() {
377 let result = IntegerReplacementType::U8.replace_for_value(Value::U64(300));
379 assert!(result.is_err());
380 }
381
382 #[test]
383 fn integer_replacement_non_integer_error() {
384 let result =
386 IntegerReplacementType::U64.replace_for_value(Value::Text("not a number".into()));
387 assert!(result.is_err());
388 }
389
390 #[test]
395 fn replace_for_bytes_identifier_32_bytes_ok() {
396 let bytes = vec![0xABu8; 32];
397 let result = ReplacementType::Identifier
398 .replace_for_bytes(bytes.clone())
399 .unwrap();
400 let expected: [u8; 32] = bytes.try_into().unwrap();
401 assert_eq!(result, Value::Identifier(expected));
402 }
403
404 #[test]
405 fn replace_for_bytes_identifier_wrong_size() {
406 let bytes = vec![0xABu8; 31]; let result = ReplacementType::Identifier.replace_for_bytes(bytes);
408 assert!(matches!(result, Err(Error::ByteLengthNot32BytesError(_))));
409 }
410
411 #[test]
412 fn replace_for_bytes_identifier_too_long() {
413 let bytes = vec![0xABu8; 33];
414 let result = ReplacementType::Identifier.replace_for_bytes(bytes);
415 assert!(matches!(result, Err(Error::ByteLengthNot32BytesError(_))));
416 }
417
418 #[test]
419 fn replace_for_bytes_binary_bytes() {
420 let bytes = vec![1, 2, 3, 4, 5];
421 let result = ReplacementType::BinaryBytes
422 .replace_for_bytes(bytes.clone())
423 .unwrap();
424 assert_eq!(result, Value::Bytes(bytes));
425 }
426
427 #[test]
428 fn replace_for_bytes_text_base58() {
429 let bytes = vec![0x01, 0x02, 0x03];
430 let expected = bs58::encode(&bytes).into_string();
431 let result = ReplacementType::TextBase58
432 .replace_for_bytes(bytes)
433 .unwrap();
434 assert_eq!(result, Value::Text(expected));
435 }
436
437 #[test]
438 fn replace_for_bytes_text_base64() {
439 let bytes = vec![0xDE, 0xAD, 0xBE, 0xEF];
440 let expected = BASE64_STANDARD.encode(&bytes);
441 let result = ReplacementType::TextBase64
442 .replace_for_bytes(bytes)
443 .unwrap();
444 assert_eq!(result, Value::Text(expected));
445 }
446
447 #[test]
452 fn replace_for_bytes_20_binary() {
453 let bytes = [0xFFu8; 20];
454 let result = ReplacementType::BinaryBytes
455 .replace_for_bytes_20(bytes)
456 .unwrap();
457 assert_eq!(result, Value::Bytes20(bytes));
458 }
459
460 #[test]
461 fn replace_for_bytes_20_text_base58() {
462 let bytes = [0x01u8; 20];
463 let expected = bs58::encode(bytes).into_string();
464 let result = ReplacementType::TextBase58
465 .replace_for_bytes_20(bytes)
466 .unwrap();
467 assert_eq!(result, Value::Text(expected));
468 }
469
470 #[test]
471 fn replace_for_bytes_20_text_base64() {
472 let bytes = [0x02u8; 20];
473 let expected = BASE64_STANDARD.encode(bytes);
474 let result = ReplacementType::TextBase64
475 .replace_for_bytes_20(bytes)
476 .unwrap();
477 assert_eq!(result, Value::Text(expected));
478 }
479
480 #[test]
481 fn replace_for_bytes_20_identifier_error() {
482 let bytes = [0xAAu8; 20];
483 let result = ReplacementType::Identifier.replace_for_bytes_20(bytes);
484 assert!(matches!(result, Err(Error::ByteLengthNot36BytesError(_))));
485 }
486
487 #[test]
492 fn replace_for_bytes_32_identifier() {
493 let bytes = [0xBBu8; 32];
494 let result = ReplacementType::Identifier
495 .replace_for_bytes_32(bytes)
496 .unwrap();
497 assert_eq!(result, Value::Identifier(bytes));
498 }
499
500 #[test]
501 fn replace_for_bytes_32_binary() {
502 let bytes = [0xCCu8; 32];
503 let result = ReplacementType::BinaryBytes
504 .replace_for_bytes_32(bytes)
505 .unwrap();
506 assert_eq!(result, Value::Bytes32(bytes));
507 }
508
509 #[test]
510 fn replace_for_bytes_32_text_base58() {
511 let bytes = [0x01u8; 32];
512 let expected = bs58::encode(bytes).into_string();
513 let result = ReplacementType::TextBase58
514 .replace_for_bytes_32(bytes)
515 .unwrap();
516 assert_eq!(result, Value::Text(expected));
517 }
518
519 #[test]
520 fn replace_for_bytes_32_text_base64() {
521 let bytes = [0x02u8; 32];
522 let expected = BASE64_STANDARD.encode(bytes);
523 let result = ReplacementType::TextBase64
524 .replace_for_bytes_32(bytes)
525 .unwrap();
526 assert_eq!(result, Value::Text(expected));
527 }
528
529 #[test]
534 fn replace_for_bytes_36_binary() {
535 let bytes = [0xDDu8; 36];
536 let result = ReplacementType::BinaryBytes
537 .replace_for_bytes_36(bytes)
538 .unwrap();
539 assert_eq!(result, Value::Bytes36(bytes));
540 }
541
542 #[test]
543 fn replace_for_bytes_36_text_base58() {
544 let bytes = [0x03u8; 36];
545 let expected = bs58::encode(bytes).into_string();
546 let result = ReplacementType::TextBase58
547 .replace_for_bytes_36(bytes)
548 .unwrap();
549 assert_eq!(result, Value::Text(expected));
550 }
551
552 #[test]
553 fn replace_for_bytes_36_text_base64() {
554 let bytes = [0x04u8; 36];
555 let expected = BASE64_STANDARD.encode(bytes);
556 let result = ReplacementType::TextBase64
557 .replace_for_bytes_36(bytes)
558 .unwrap();
559 assert_eq!(result, Value::Text(expected));
560 }
561
562 #[test]
563 fn replace_for_bytes_36_identifier_error() {
564 let bytes = [0xEEu8; 36];
565 let result = ReplacementType::Identifier.replace_for_bytes_36(bytes);
566 assert!(matches!(result, Err(Error::ByteLengthNot36BytesError(_))));
567 }
568
569 #[test]
574 fn replace_at_path_single_segment_bytes32() {
575 let bytes = [0xABu8; 32];
576 let mut map = BTreeMap::new();
577 map.insert("id".to_string(), Value::Bytes32(bytes));
578
579 map.replace_at_path("id", ReplacementType::Identifier)
580 .unwrap();
581 assert_eq!(map.get("id"), Some(&Value::Identifier(bytes)));
582 }
583
584 #[test]
585 fn replace_at_path_single_segment_bytes20() {
586 let bytes = [0x11u8; 20];
587 let mut map = BTreeMap::new();
588 map.insert("addr".to_string(), Value::Bytes20(bytes));
589
590 map.replace_at_path("addr", ReplacementType::BinaryBytes)
591 .unwrap();
592 assert_eq!(map.get("addr"), Some(&Value::Bytes20(bytes)));
593 }
594
595 #[test]
596 fn replace_at_path_single_segment_bytes36() {
597 let bytes = [0x22u8; 36];
598 let mut map = BTreeMap::new();
599 map.insert("outpoint".to_string(), Value::Bytes36(bytes));
600
601 map.replace_at_path("outpoint", ReplacementType::BinaryBytes)
602 .unwrap();
603 assert_eq!(map.get("outpoint"), Some(&Value::Bytes36(bytes)));
604 }
605
606 #[test]
607 fn replace_at_path_single_segment_identifier_to_base58() {
608 let bytes = [0xCCu8; 32];
609 let mut map = BTreeMap::new();
610 map.insert("id".to_string(), Value::Identifier(bytes));
611
612 map.replace_at_path("id", ReplacementType::TextBase58)
613 .unwrap();
614 let expected = bs58::encode(bytes).into_string();
615 assert_eq!(map.get("id"), Some(&Value::Text(expected)));
616 }
617
618 #[test]
623 fn replace_at_path_nested() {
624 let bytes = [0xFFu8; 32];
625 let inner_map = vec![(Value::Text("nested_id".into()), Value::Bytes32(bytes))];
626 let mut map = BTreeMap::new();
627 map.insert("parent".to_string(), Value::Map(inner_map));
628
629 map.replace_at_path("parent.nested_id", ReplacementType::Identifier)
630 .unwrap();
631
632 let parent = map.get("parent").unwrap();
633 if let Value::Map(inner) = parent {
634 let val = inner.get_optional_key("nested_id").unwrap();
635 assert_eq!(*val, Value::Identifier(bytes));
636 } else {
637 panic!("expected Map");
638 }
639 }
640
641 #[test]
642 fn replace_at_path_deep_nested() {
643 let bytes = [0xAAu8; 32];
644 let level2 = vec![(Value::Text("deep_id".into()), Value::Bytes32(bytes))];
645 let level1 = vec![(Value::Text("level2".into()), Value::Map(level2))];
646 let mut map = BTreeMap::new();
647 map.insert("level1".to_string(), Value::Map(level1));
648
649 map.replace_at_path("level1.level2.deep_id", ReplacementType::Identifier)
650 .unwrap();
651
652 let l1 = map.get("level1").unwrap();
653 if let Value::Map(l1_map) = l1 {
654 let l2 = l1_map.get_optional_key("level2").unwrap();
655 if let Value::Map(l2_map) = l2 {
656 let val = l2_map.get_optional_key("deep_id").unwrap();
657 assert_eq!(*val, Value::Identifier(bytes));
658 } else {
659 panic!("expected Map at level2");
660 }
661 } else {
662 panic!("expected Map at level1");
663 }
664 }
665
666 #[test]
671 fn replace_at_path_through_array_applies_to_elements() {
672 let bytes1 = [0x11u8; 32];
687 let bytes2 = [0x22u8; 32];
688 let item1 = Value::Map(vec![(Value::Text("id".into()), Value::Bytes32(bytes1))]);
689 let item2 = Value::Map(vec![(Value::Text("id".into()), Value::Bytes32(bytes2))]);
690 let wrapper_map = vec![(Value::Text("arr".into()), Value::Array(vec![item1, item2]))];
691 let mut map = BTreeMap::new();
692 map.insert("wrapper".to_string(), Value::Map(wrapper_map));
693
694 map.replace_at_path("wrapper.arr.placeholder.id", ReplacementType::Identifier)
696 .unwrap();
697
698 if let Value::Map(wrapper) = map.get("wrapper").unwrap() {
699 let arr_val = wrapper.get_optional_key("arr").unwrap();
700 if let Value::Array(arr) = arr_val {
701 assert_eq!(arr.len(), 2);
702 for (i, item) in arr.iter().enumerate() {
703 if let Value::Map(m) = item {
704 let val = m.get_optional_key("id").unwrap();
705 let expected_bytes = if i == 0 { bytes1 } else { bytes2 };
706 assert_eq!(*val, Value::Identifier(expected_bytes));
707 } else {
708 panic!("expected Map in array");
709 }
710 }
711 } else {
712 panic!("expected Array");
713 }
714 } else {
715 panic!("expected Map at wrapper");
716 }
717 }
718
719 fn base58_id(seed: u8) -> Value {
724 Value::Text(Identifier::from([seed; 32]).to_string(Encoding::Base58))
725 }
726
727 #[test]
728 fn should_replace_the_members_of_a_top_level_list() {
729 let mut map = BTreeMap::new();
730 map.insert(
731 "ids".to_string(),
732 Value::Array(vec![base58_id(1), Value::Bytes32([2u8; 32])]),
733 );
734 map.replace_at_path("ids[]", ReplacementType::Identifier)
735 .unwrap();
736 assert_eq!(
737 map.get("ids").unwrap(),
738 &Value::Array(vec![
739 Value::Identifier([1u8; 32]),
740 Value::Identifier([2u8; 32])
741 ])
742 );
743
744 map.insert(
746 "more".to_string(),
747 Value::Array(vec![base58_id(3), Value::Text("keep".into())]),
748 );
749 map.replace_at_path("more[0]", ReplacementType::Identifier)
750 .unwrap();
751 assert_eq!(
752 map.get("more").unwrap(),
753 &Value::Array(vec![
754 Value::Identifier([3u8; 32]),
755 Value::Text("keep".into())
756 ])
757 );
758 assert!(map
759 .replace_at_path("more[2]", ReplacementType::Identifier)
760 .is_err());
761 }
762
763 #[test]
764 fn should_replace_the_members_of_a_nested_list_and_descend_into_a_list_of_maps() {
765 let inner = Value::Map(vec![(
766 Value::Text("ids".into()),
767 Value::Array(vec![base58_id(4)]),
768 )]);
769 let mut map = BTreeMap::new();
770 map.insert("meta".to_string(), inner);
771 map.replace_at_path("meta.ids[]", ReplacementType::Identifier)
772 .unwrap();
773 let Some(Value::Map(meta)) = map.get("meta") else {
774 panic!("expected the map");
775 };
776 assert_eq!(
777 meta.get_optional_key("ids").unwrap(),
778 &Value::Array(vec![Value::Identifier([4u8; 32])])
779 );
780
781 let item = Value::Map(vec![(Value::Text("id".into()), base58_id(5))]);
782 map.insert("items".to_string(), Value::Array(vec![item]));
783 map.replace_at_path("items[].id", ReplacementType::Identifier)
784 .unwrap();
785 let Some(Value::Array(items)) = map.get("items") else {
786 panic!("expected the list");
787 };
788 let Value::Map(item) = &items[0] else {
789 panic!("expected a map member");
790 };
791 assert_eq!(
792 item.get_optional_key("id").unwrap(),
793 &Value::Identifier([5u8; 32])
794 );
795 }
796
797 #[test]
798 fn should_treat_an_absent_top_level_list_as_nothing_to_replace() {
799 let mut map = BTreeMap::new();
800 map.insert("a".to_string(), Value::U32(1));
801 assert!(map
802 .replace_at_path("ids[]", ReplacementType::Identifier)
803 .is_ok());
804 assert!(map
806 .replace_at_path("a.ids[]", ReplacementType::Identifier)
807 .is_err());
808 }
809
810 #[test]
815 fn replace_at_path_empty_path_error() {
816 let mut map = BTreeMap::new();
817 map.insert("key".to_string(), Value::U64(1));
818 let result = map.replace_at_path("", ReplacementType::Identifier);
819 assert!(result.is_ok());
823 }
824
825 #[test]
826 fn replace_at_path_missing_key_returns_ok() {
827 let mut map = BTreeMap::new();
828 map.insert("key".to_string(), Value::U64(1));
829 let result = map.replace_at_path("nonexistent", ReplacementType::BinaryBytes);
831 assert!(result.is_ok());
832 }
833
834 #[test]
835 fn replace_at_path_non_map_value_in_nested_path_error() {
836 let mut map = BTreeMap::new();
837 map.insert("key".to_string(), Value::U64(42));
838 let result = map.replace_at_path("key.sub", ReplacementType::BinaryBytes);
840 assert!(matches!(result, Err(Error::PathError(_))));
841 }
842
843 #[test]
848 fn replace_at_paths_multiple() {
849 let bytes1 = [0xAAu8; 32];
850 let bytes2 = [0xBBu8; 32];
851 let mut map = BTreeMap::new();
852 map.insert("id1".to_string(), Value::Bytes32(bytes1));
853 map.insert("id2".to_string(), Value::Bytes32(bytes2));
854
855 let paths = vec!["id1".to_string(), "id2".to_string()];
856 map.replace_at_paths(&paths, ReplacementType::Identifier)
857 .unwrap();
858
859 assert_eq!(map.get("id1"), Some(&Value::Identifier(bytes1)));
860 assert_eq!(map.get("id2"), Some(&Value::Identifier(bytes2)));
861 }
862
863 #[test]
868 fn replace_consume_value_identifier_to_base58() {
869 let bytes = [0xCCu8; 32];
870 let val = Value::Identifier(bytes);
871 let result = ReplacementType::TextBase58
872 .replace_consume_value(val)
873 .unwrap();
874 let expected = bs58::encode(bytes).into_string();
875 assert_eq!(result, Value::Text(expected));
876 }
877
878 #[test]
879 fn replace_value_in_place_identifier_to_binary() {
880 let bytes = [0xDDu8; 32];
881 let mut val = Value::Identifier(bytes);
882 ReplacementType::BinaryBytes
883 .replace_value_in_place(&mut val)
884 .unwrap();
885 assert_eq!(val, Value::Bytes(bytes.to_vec()));
886 }
887}