platform_value/btreemap_extensions/
btreemap_field_replacement.rs1use crate::value_map::ValueMapHelper;
2use crate::{Error, Value};
3use std::collections::BTreeMap;
4
5use base64::prelude::BASE64_STANDARD;
6use base64::Engine;
7use std::iter::Peekable;
8use std::vec::IntoIter;
9
10#[derive(Debug, Clone, Copy)]
11pub enum IntegerReplacementType {
12 U128,
13 I128,
14 U64,
15 I64,
16 U32,
17 I32,
18 U16,
19 I16,
20 U8,
21 I8,
22}
23
24impl IntegerReplacementType {
25 pub fn replace_for_value(&self, value: Value) -> Result<Value, Error> {
26 Ok(match self {
27 IntegerReplacementType::U128 => Value::U128(value.try_into()?),
28 IntegerReplacementType::I128 => Value::I128(value.try_into()?),
29 IntegerReplacementType::U64 => Value::U64(value.try_into()?),
30 IntegerReplacementType::I64 => Value::I64(value.try_into()?),
31 IntegerReplacementType::U32 => Value::U32(value.try_into()?),
32 IntegerReplacementType::I32 => Value::I32(value.try_into()?),
33 IntegerReplacementType::U16 => Value::U16(value.try_into()?),
34 IntegerReplacementType::I16 => Value::I16(value.try_into()?),
35 IntegerReplacementType::U8 => Value::U8(value.try_into()?),
36 IntegerReplacementType::I8 => Value::I8(value.try_into()?),
37 })
38 }
39}
40
41#[derive(Debug, Clone, Copy)]
42pub enum ReplacementType {
43 Identifier,
44 BinaryBytes,
45 TextBase58,
46 TextBase64,
47}
48
49impl ReplacementType {
50 pub fn replace_for_bytes(&self, bytes: Vec<u8>) -> Result<Value, Error> {
51 match self {
52 ReplacementType::Identifier => {
53 Ok(Value::Identifier(bytes.try_into().map_err(|_| {
54 Error::ByteLengthNot32BytesError(String::from(
55 "Trying to replace into an identifier, but not 32 bytes long",
56 ))
57 })?))
58 }
59 ReplacementType::BinaryBytes => Ok(Value::Bytes(bytes)),
60 ReplacementType::TextBase58 => Ok(Value::Text(bs58::encode(bytes).into_string())),
61 ReplacementType::TextBase64 => Ok(Value::Text(BASE64_STANDARD.encode(bytes))),
62 }
63 }
64
65 pub fn replace_for_bytes_20(&self, bytes: [u8; 20]) -> Result<Value, Error> {
66 match self {
67 ReplacementType::BinaryBytes => Ok(Value::Bytes20(bytes)),
68 ReplacementType::TextBase58 => Ok(Value::Text(bs58::encode(bytes).into_string())),
69 ReplacementType::TextBase64 => Ok(Value::Text(BASE64_STANDARD.encode(bytes))),
70 _ => Err(Error::ByteLengthNot36BytesError(
71 "trying to replace 36 bytes into an identifier".to_string(),
72 )),
73 }
74 }
75
76 pub fn replace_for_bytes_32(&self, bytes: [u8; 32]) -> Result<Value, Error> {
77 match self {
78 ReplacementType::Identifier => Ok(Value::Identifier(bytes)),
79 ReplacementType::BinaryBytes => Ok(Value::Bytes32(bytes)),
80 ReplacementType::TextBase58 => Ok(Value::Text(bs58::encode(bytes).into_string())),
81 ReplacementType::TextBase64 => Ok(Value::Text(BASE64_STANDARD.encode(bytes))),
82 }
83 }
84
85 pub fn replace_for_bytes_36(&self, bytes: [u8; 36]) -> Result<Value, Error> {
86 match self {
87 ReplacementType::BinaryBytes => Ok(Value::Bytes36(bytes)),
88 ReplacementType::TextBase58 => Ok(Value::Text(bs58::encode(bytes).into_string())),
89 ReplacementType::TextBase64 => Ok(Value::Text(BASE64_STANDARD.encode(bytes))),
90 _ => Err(Error::ByteLengthNot36BytesError(
91 "trying to replace 36 bytes into an identifier".to_string(),
92 )),
93 }
94 }
95
96 pub fn replace_consume_value(&self, value: Value) -> Result<Value, Error> {
97 let bytes = value.into_identifier_bytes()?;
98 self.replace_for_bytes(bytes)
99 }
100
101 pub fn replace_value_in_place(&self, value: &mut Value) -> Result<(), Error> {
102 let bytes = value.take().into_identifier_bytes()?;
103 *value = self.replace_for_bytes(bytes)?;
104 Ok(())
105 }
106}
107
108pub trait BTreeValueMapReplacementPathHelper {
109 fn replace_at_path(
110 &mut self,
111 path: &str,
112 replacement_type: ReplacementType,
113 ) -> Result<(), Error>;
114 fn replace_at_paths<'a, I: IntoIterator<Item = &'a String>>(
115 &mut self,
116 paths: I,
117 replacement_type: ReplacementType,
118 ) -> Result<(), Error>;
119}
120
121fn replace_down(
122 mut current_values: Vec<&mut Value>,
123 mut split: Peekable<IntoIter<&str>>,
124 replacement_type: ReplacementType,
125) -> Result<(), Error> {
126 if let Some(path_component) = split.next() {
127 let next_values = current_values
128 .iter_mut()
129 .map(|current_value| {
130 if current_value.is_map() {
131 let map = current_value.as_map_mut_ref()?;
132 let Some(new_value) = map.get_optional_key_mut(path_component) else {
133 return Ok(None);
134 };
135 if split.peek().is_none() {
136 match new_value {
137 Value::Bytes20(bytes) => {
138 *new_value = replacement_type.replace_for_bytes_20(*bytes)?;
139 }
140 Value::Bytes32(bytes) => {
141 *new_value = replacement_type.replace_for_bytes_32(*bytes)?;
142 }
143 Value::Bytes36(bytes) => {
144 *new_value = replacement_type.replace_for_bytes_36(*bytes)?;
145 }
146 _ => {
147 let bytes = match replacement_type {
148 ReplacementType::Identifier | ReplacementType::TextBase58 => {
149 new_value.to_identifier_bytes()
150 }
151 ReplacementType::BinaryBytes | ReplacementType::TextBase64 => {
152 new_value.to_binary_bytes()
153 }
154 }?;
155 *new_value = replacement_type.replace_for_bytes(bytes)?;
156 }
157 }
158 Ok(None)
159 } else {
160 Ok(Some(vec![new_value]))
161 }
162 } else if current_value.is_array() {
163 let array = current_value.to_array_mut()?.iter_mut().collect();
165 Ok(Some(array))
166 } else {
167 Err(Error::PathError("path was not an array or map".to_string()))
168 }
169 })
170 .collect::<Result<Vec<_>, Error>>()?
171 .into_iter()
172 .flatten()
173 .flatten()
174 .collect();
175 replace_down(next_values, split, replacement_type)
176 } else {
177 Ok(())
178 }
179}
180
181impl BTreeValueMapReplacementPathHelper for BTreeMap<String, Value> {
182 fn replace_at_path(
183 &mut self,
184 path: &str,
185 replacement_type: ReplacementType,
186 ) -> Result<(), Error> {
187 let mut split: Vec<_> = path.split('.').collect();
188 let first = split.first();
189 let Some(first_path_component) = first else {
190 return Err(Error::PathError("path was empty".to_string()));
191 };
192 let Some(current_value) = self.get_mut(first_path_component.to_owned()) else {
193 return Ok(());
194 };
195 if split.len() == 1 {
196 match current_value {
197 Value::Bytes20(bytes) => {
198 *current_value = replacement_type.replace_for_bytes_20(*bytes)?;
199 }
200 Value::Bytes32(bytes) => {
201 *current_value = replacement_type.replace_for_bytes_32(*bytes)?;
202 }
203 Value::Bytes36(bytes) => {
204 *current_value = replacement_type.replace_for_bytes_36(*bytes)?;
205 }
206 _ => {
207 let bytes = match replacement_type {
208 ReplacementType::Identifier | ReplacementType::TextBase58 => {
209 current_value.to_identifier_bytes()
210 }
211 ReplacementType::BinaryBytes | ReplacementType::TextBase64 => {
212 current_value.to_binary_bytes()
213 }
214 }?;
215 *current_value = replacement_type.replace_for_bytes(bytes)?;
216 }
217 }
218 Ok(())
219 } else {
220 split.remove(0);
221 let current_values = vec![current_value];
222 replace_down(
224 current_values,
225 split.into_iter().peekable(),
226 replacement_type,
227 )
228 }
229 }
230
231 fn replace_at_paths<'a, I: IntoIterator<Item = &'a String>>(
232 &mut self,
233 paths: I,
234 replacement_type: ReplacementType,
235 ) -> Result<(), Error> {
236 paths
237 .into_iter()
238 .try_for_each(|path| self.replace_at_path(path.as_str(), replacement_type))
239 }
240}