platform_value/replace.rs
1use crate::btreemap_extensions::btreemap_field_replacement::IntegerReplacementType;
2use crate::inner_value_at_path::is_array_path;
3use crate::{Error, ReplacementType, Value, ValueMapHelper};
4use std::collections::HashSet;
5
6impl Value {
7 /// If the `Value` is a `Map`, replaces the value at the path inside the map.
8 /// This is used to set inner values as Identifiers or BinaryData, or from Identifiers or
9 /// BinaryData to base58 or base64 strings.
10 /// Either returns `Err(Error::Structure("reason"))` or `Err(Error::ByteLengthNot32BytesError))`
11 /// if the replacement can not happen.
12 ///
13 /// ```
14 /// # use platform_value::{Error, Identifier, ReplacementType, Value};
15 /// #
16 /// let mut inner_value = Value::Map(
17 /// vec![
18 /// (Value::Text(String::from("food_id")), Value::Text("6oFRdsUNiAtXscRn52atKYCiF8RBnH9vbUzhtzY3d83e".to_string())),
19 /// ]
20 /// );
21 /// let mut value = Value::Map(
22 /// vec![
23 /// (Value::Text(String::from("foods")), inner_value),
24 /// ]
25 /// );
26 ///
27 /// value.replace_at_path("foods.food_id", ReplacementType::Identifier).expect("expected to replace at path with identifier");
28 ///
29 /// assert_eq!(value.get_value_at_path("foods.food_id"), Ok(&Value::Identifier([86, 35, 118, 67, 167, 43, 101, 109, 72, 97, 35, 99, 0, 254, 108, 154, 254, 154, 190, 40, 237, 25, 58, 246, 111, 19, 44, 215, 141, 140, 156, 117])));
30 ///
31 /// let mut tangerine_value = Value::Map(
32 /// vec![
33 /// (Value::Text(String::from("food_id")), Value::Text("6oFRdsUNiAtXscRn52atKYCiF8RBnH9vbUzhtzY3d83e".to_string())),
34 /// ]
35 /// );
36 /// let mut mandarin_value = Value::Map(
37 /// vec![
38 /// (Value::Text(String::from("food_id")), Value::Text("6oFRdsUNiAtXscRn52atKYCiF8RBnH9vbUzhtzY3d83e".to_string())),
39 /// ]
40 /// );
41 /// let mut oranges_value = Value::Array(
42 /// vec![
43 /// tangerine_value,
44 /// mandarin_value
45 /// ]
46 /// );
47 /// let mut value = Value::Map(
48 /// vec![
49 /// (Value::Text(String::from("foods")), oranges_value),
50 /// ]
51 /// );
52 ///
53 /// value.replace_at_path("foods[].food_id", ReplacementType::Identifier).expect("expected to replace at path with identifier");
54 ///
55 /// assert_eq!(value.get_value_at_path("foods[0].food_id"), Ok(&Value::Identifier([86, 35, 118, 67, 167, 43, 101, 109, 72, 97, 35, 99, 0, 254, 108, 154, 254, 154, 190, 40, 237, 25, 58, 246, 111, 19, 44, 215, 141, 140, 156, 117])));
56 ///
57 /// ```
58 pub fn replace_at_path(
59 &mut self,
60 path: &str,
61 replacement_type: ReplacementType,
62 ) -> Result<(), Error> {
63 let mut split = path.split('.').peekable();
64 let mut current_values = vec![self];
65 while let Some(path_component) = split.next() {
66 if let Some((string_part, number_part)) = is_array_path(path_component)? {
67 current_values = current_values
68 .into_iter()
69 .map(|current_value| {
70 let map = current_value.to_map_mut()?;
71 let array_value = map.get_key_mut(string_part)?;
72 let array = array_value.to_array_mut()?;
73 if let Some(number_part) = number_part {
74 if array.len() < number_part {
75 //this already exists
76 Ok(vec![array.get_mut(number_part).unwrap()])
77 } else {
78 Err(Error::StructureError(format!(
79 "element at position {number_part} in array does not exist"
80 )))
81 }
82 } else {
83 // we are replacing all members in array
84 Ok(array.iter_mut().collect())
85 }
86 })
87 .collect::<Result<Vec<Vec<&mut Value>>, Error>>()?
88 .into_iter()
89 .flatten()
90 .collect()
91 } else {
92 current_values = current_values
93 .into_iter()
94 .filter_map(|current_value| {
95 let map = match current_value.as_map_mut_ref() {
96 Ok(map) => map,
97 Err(err) => return Some(Err(err)),
98 };
99
100 let new_value = map.get_optional_key_mut(path_component)?;
101
102 if split.peek().is_none() {
103 let bytes_result = match replacement_type {
104 ReplacementType::Identifier | ReplacementType::TextBase58 => {
105 new_value.to_identifier_bytes()
106 }
107 ReplacementType::BinaryBytes | ReplacementType::TextBase64 => {
108 new_value.to_binary_bytes()
109 }
110 };
111 let bytes = match bytes_result {
112 Ok(bytes) => bytes,
113 Err(err) => return Some(Err(err)),
114 };
115 *new_value = match replacement_type.replace_for_bytes(bytes) {
116 Ok(value) => value,
117 Err(err) => return Some(Err(err)),
118 };
119 return None;
120 }
121 Some(Ok(new_value))
122 })
123 .collect::<Result<Vec<&mut Value>, Error>>()?;
124 }
125 }
126 Ok(())
127 }
128
129 /// Calls replace_at_path for every path in a given array.
130 /// Either returns `Err(Error::Structure("reason"))` or `Err(Error::ByteLengthNot32BytesError))`
131 /// if the replacement can not happen.
132 ///
133 /// ```
134 /// # use platform_value::{Error, Identifier, ReplacementType, Value};
135 /// #
136 /// let mut inner_value = Value::Map(
137 /// vec![
138 /// (Value::Text(String::from("grapes")), Value::Text("6oFRdsUNiAtXscRn52atKYCiF8RBnH9vbUzhtzY3d83e".to_string())),
139 /// (Value::Text(String::from("oranges")), Value::Array(vec![Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101)])),
140 /// ]
141 /// );
142 /// let mut value = Value::Map(
143 /// vec![
144 /// (Value::Text(String::from("foods")), inner_value),
145 /// ]
146 /// );
147 ///
148 /// let paths = vec!["foods.grapes", "foods.oranges"];
149 ///
150 /// value.replace_at_paths(paths, ReplacementType::Identifier).expect("expected to replace at paths with identifier");
151 ///
152 /// assert_eq!(value.get_value_at_path("foods.grapes"), Ok(&Value::Identifier([86, 35, 118, 67, 167, 43, 101, 109, 72, 97, 35, 99, 0, 254, 108, 154, 254, 154, 190, 40, 237, 25, 58, 246, 111, 19, 44, 215, 141, 140, 156, 117])));
153 /// assert_eq!(value.get_value_at_path("foods.oranges"), Ok(&Value::Identifier([104, 101, 108, 104, 101, 108, 104, 101, 108, 104, 101, 108, 104, 101, 108, 104, 101, 108, 104, 101, 108, 104, 101, 108, 104, 101, 108, 104, 101, 108, 104, 101])));
154 ///
155 /// ```
156 pub fn replace_at_paths<'a, I: IntoIterator<Item = &'a str>>(
157 &mut self,
158 paths: I,
159 replacement_type: ReplacementType,
160 ) -> Result<(), Error> {
161 paths
162 .into_iter()
163 .try_for_each(|path| self.replace_at_path(path, replacement_type))
164 }
165
166 /// If the `Value` is a `Map`, replaces the value at the path inside the map.
167 /// This is used to set inner values as Identifiers or BinaryData, or from Identifiers or
168 /// BinaryData to base58 or base64 strings.
169 /// Either returns `Err(Error::Structure("reason"))` or `Err(Error::ByteLengthNot32BytesError))`
170 /// if the replacement can not happen.
171 ///
172 /// ```
173 /// # use platform_value::{Error, Identifier, IntegerReplacementType, Value};
174 /// #
175 /// let mut inner_value = Value::Map(
176 /// vec![
177 /// (Value::Text(String::from("food_id")), Value::U8(5)),
178 /// ]
179 /// );
180 /// let mut value = Value::Map(
181 /// vec![
182 /// (Value::Text(String::from("foods")), inner_value),
183 /// ]
184 /// );
185 ///
186 /// value.replace_integer_type_at_path("foods.food_id", IntegerReplacementType::U32).expect("expected to replace at path with identifier");
187 ///
188 /// assert_eq!(value.get_value_at_path("foods.food_id"), Ok(&Value::U32(5)));
189 ///
190 /// let mut tangerine_value = Value::Map(
191 /// vec![
192 /// (Value::Text(String::from("food_id")), Value::U128(8)),
193 /// ]
194 /// );
195 /// let mut mandarin_value = Value::Map(
196 /// vec![
197 /// (Value::Text(String::from("food_id")), Value::U32(2)),
198 /// ]
199 /// );
200 /// let mut oranges_value = Value::Array(
201 /// vec![
202 /// tangerine_value,
203 /// mandarin_value
204 /// ]
205 /// );
206 /// let mut value = Value::Map(
207 /// vec![
208 /// (Value::Text(String::from("foods")), oranges_value),
209 /// ]
210 /// );
211 ///
212 /// value.replace_integer_type_at_path("foods[].food_id", IntegerReplacementType::U16).expect("expected to replace at path with identifier");
213 ///
214 /// assert_eq!(value.get_value_at_path("foods[0].food_id"), Ok(&Value::U16(8)));
215 ///
216 /// ```
217 pub fn replace_integer_type_at_path(
218 &mut self,
219 path: &str,
220 replacement_type: IntegerReplacementType,
221 ) -> Result<(), Error> {
222 let mut split = path.split('.').peekable();
223 let mut current_values = vec![self];
224 while let Some(path_component) = split.next() {
225 if let Some((string_part, number_part)) = is_array_path(path_component)? {
226 current_values = current_values
227 .into_iter()
228 .map(|current_value| {
229 let map = current_value.to_map_mut()?;
230 let array_value = map.get_key_mut(string_part)?;
231 let array = array_value.to_array_mut()?;
232 if let Some(number_part) = number_part {
233 if array.len() < number_part {
234 //this already exists
235 Ok(vec![array.get_mut(number_part).unwrap()])
236 } else {
237 Err(Error::StructureError(format!(
238 "element at position {number_part} in array does not exist"
239 )))
240 }
241 } else {
242 // we are replacing all members in array
243 Ok(array.iter_mut().collect())
244 }
245 })
246 .collect::<Result<Vec<Vec<&mut Value>>, Error>>()?
247 .into_iter()
248 .flatten()
249 .collect()
250 } else {
251 current_values = current_values
252 .into_iter()
253 .filter_map(|current_value| {
254 let map = match current_value.as_map_mut_ref() {
255 Ok(map) => map,
256 Err(err) => return Some(Err(err)),
257 };
258
259 let new_value = map.get_optional_key_mut(path_component)?;
260
261 if split.peek().is_none() {
262 *new_value = match replacement_type.replace_for_value(new_value.clone())
263 {
264 Ok(value) => value,
265 Err(err) => return Some(Err(err)),
266 };
267 return None;
268 }
269 Some(Ok(new_value))
270 })
271 .collect::<Result<Vec<&mut Value>, Error>>()?;
272 }
273 }
274 Ok(())
275 }
276
277 /// Calls replace_at_path for every path in a given array.
278 /// Either returns `Err(Error::Structure("reason"))` or `Err(Error::ByteLengthNot32BytesError))`
279 /// if the replacement can not happen.
280 ///
281 /// ```
282 /// # use platform_value::{Error, Identifier, IntegerReplacementType, ReplacementType, Value};
283 /// #
284 /// let mut inner_value = Value::Map(
285 /// vec![
286 /// (Value::Text(String::from("grapes")), Value::U16(5)),
287 /// (Value::Text(String::from("oranges")), Value::I32(6)),
288 /// ]
289 /// );
290 /// let mut value = Value::Map(
291 /// vec![
292 /// (Value::Text(String::from("foods")), inner_value),
293 /// ]
294 /// );
295 ///
296 /// let paths = vec!["foods.grapes", "foods.oranges"];
297 ///
298 /// value.replace_integer_type_at_paths(paths, IntegerReplacementType::U32).expect("expected to replace at paths with identifier");
299 ///
300 /// assert_eq!(value.get_value_at_path("foods.grapes"), Ok(&Value::U32(5)));
301 /// assert_eq!(value.get_value_at_path("foods.oranges"), Ok(&Value::U32(6)));
302 ///
303 /// ```
304 pub fn replace_integer_type_at_paths<'a, I: IntoIterator<Item = &'a str>>(
305 &mut self,
306 paths: I,
307 replacement_type: IntegerReplacementType,
308 ) -> Result<(), Error> {
309 paths
310 .into_iter()
311 .try_for_each(|path| self.replace_integer_type_at_path(path, replacement_type))
312 }
313
314 /// `replace_to_binary_types_when_setting_with_path` will replace a value with a corresponding
315 /// binary type (Identifier or Binary Data) if that data is in one of the given paths.
316 /// Paths can either be terminal, or can represent an object or an array (with values) where
317 /// all subvalues must be set to the binary type.
318 /// Either returns `Err(Error::Structure("reason"))` or `Err(Error::ByteLengthNot32BytesError))`
319 /// if the replacement can not happen.
320 ///
321 /// ```
322 /// # use std::collections::HashSet;
323 /// use platform_value::{Error, Identifier, ReplacementType, Value};
324 /// #
325 /// let mut inner_inner_value = Value::Map(
326 /// vec![
327 /// (Value::Text(String::from("mandarins")), Value::Text("6oFRdsUNiAtXscRn52atKYCiF8RBnH9vbUzhtzY3d83e".to_string())),
328 /// (Value::Text(String::from("tangerines")), Value::Array(vec![Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101)])),
329 /// ]
330 /// );
331 /// let mut inner_value = Value::Map(
332 /// vec![
333 /// (Value::Text(String::from("grapes")), Value::Text("6oFRdsUNiAtXscRn52atKYCiF8RBnH9vbUzhtzY3d83e".to_string())),
334 /// (Value::Text(String::from("oranges")), inner_inner_value),
335 /// ]
336 /// );
337 /// let mut value = Value::Map(
338 /// vec![
339 /// (Value::Text(String::from("foods")), inner_value),
340 /// ]
341 /// );
342 ///
343 ///
344 /// let identifier_paths = HashSet::from(["foods.oranges.tangerines"]);
345 ///
346 /// value.replace_to_binary_types_of_root_value_when_setting_at_path("foods.oranges", identifier_paths, HashSet::new()).expect("expected to replace at paths with identifier");
347 ///
348 /// assert_eq!(value.get_value_at_path("foods.oranges.tangerines"), Ok(&Value::Identifier([104, 101, 108, 104, 101, 108, 104, 101, 108, 104, 101, 108, 104, 101, 108, 104, 101, 108, 104, 101, 108, 104, 101, 108, 104, 101, 108, 104, 101, 108, 104, 101])));
349 ///
350 /// ```
351 pub fn replace_to_binary_types_of_root_value_when_setting_at_path(
352 &mut self,
353 path: &str,
354 identifier_paths: HashSet<&str>,
355 binary_paths: HashSet<&str>,
356 ) -> Result<(), Error> {
357 if identifier_paths.contains(path) {
358 ReplacementType::Identifier.replace_value_in_place(self)?;
359 } else if binary_paths.contains(path) {
360 ReplacementType::BinaryBytes.replace_value_in_place(self)?;
361 } else {
362 identifier_paths
363 .into_iter()
364 .try_for_each(|identifier_path| {
365 if identifier_path.starts_with(path) {
366 self.replace_at_path(identifier_path, ReplacementType::Identifier)
367 .map(|_| ())
368 } else {
369 Ok(())
370 }
371 })?;
372
373 binary_paths.into_iter().try_for_each(|binary_path| {
374 if binary_path.starts_with(path) {
375 self.replace_at_path(binary_path, ReplacementType::BinaryBytes)
376 .map(|_| ())
377 } else {
378 Ok(())
379 }
380 })?;
381 }
382 Ok(())
383 }
384
385 /// `replace_to_binary_types_when_setting_with_path` will replace a value with a corresponding
386 /// binary type (Identifier or Binary Data) if that data is in one of the given paths.
387 /// Paths can either be terminal, or can represent an object or an array (with values) where
388 /// all subvalues must be set to the binary type.
389 /// Either returns `Err(Error::Structure("reason"))` or `Err(Error::ByteLengthNot32BytesError))`
390 /// if the replacement can not happen.
391 ///
392 /// ```
393 /// # use std::collections::HashSet;
394 /// use platform_value::{Error, Identifier, ReplacementType, Value};
395 /// #
396 /// let mut inner_inner_value = Value::Map(
397 /// vec![
398 /// (Value::Text(String::from("mandarins")), Value::Text("6oFRdsUNiAtXscRn52atKYCiF8RBnH9vbUzhtzY3d83e".to_string())),
399 /// (Value::Text(String::from("tangerines")), Value::Array(vec![Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101), Value::U8(108),Value::U8(104), Value::U8(101)])),
400 /// ]
401 /// );
402 /// let mut inner_value = Value::Map(
403 /// vec![
404 /// (Value::Text(String::from("grapes")), Value::Text("6oFRdsUNiAtXscRn52atKYCiF8RBnH9vbUzhtzY3d83e".to_string())),
405 /// (Value::Text(String::from("oranges")), inner_inner_value),
406 /// ]
407 /// );
408 /// let mut value = Value::Map(
409 /// vec![
410 /// (Value::Text(String::from("foods")), inner_value),
411 /// ]
412 /// );
413 ///
414 ///
415 /// let identifier_paths = HashSet::from(["foods.oranges.tangerines"]);
416 ///
417 /// let oranges = value.get_mut_value_at_path("foods.oranges").unwrap();
418 /// oranges.replace_to_binary_types_when_setting_with_path("foods.oranges", identifier_paths, HashSet::new()).expect("expected to replace at paths with identifier");
419 ///
420 /// assert_eq!(value.get_value_at_path("foods.oranges.tangerines"), Ok(&Value::Identifier([104, 101, 108, 104, 101, 108, 104, 101, 108, 104, 101, 108, 104, 101, 108, 104, 101, 108, 104, 101, 108, 104, 101, 108, 104, 101, 108, 104, 101, 108, 104, 101])));
421 ///
422 /// ```
423 pub fn replace_to_binary_types_when_setting_with_path(
424 &mut self,
425 path: &str,
426 identifier_paths: HashSet<&str>,
427 binary_paths: HashSet<&str>,
428 ) -> Result<(), Error> {
429 if identifier_paths.contains(path) {
430 ReplacementType::Identifier.replace_value_in_place(self)?;
431 } else if binary_paths.contains(path) {
432 ReplacementType::BinaryBytes.replace_value_in_place(self)?;
433 } else {
434 let mut path = path.to_string();
435 path.push('.');
436 identifier_paths
437 .into_iter()
438 .try_for_each(|identifier_path| {
439 if let Some(suffix) = identifier_path.strip_prefix(path.as_str()) {
440 self.replace_at_path(suffix, ReplacementType::Identifier)
441 .map(|_| ())
442 } else {
443 Ok(())
444 }
445 })?;
446 binary_paths.into_iter().try_for_each(|binary_path| {
447 if let Some(suffix) = binary_path.strip_prefix(path.as_str()) {
448 self.replace_at_path(suffix, ReplacementType::BinaryBytes)
449 .map(|_| ())
450 } else {
451 Ok(())
452 }
453 })?;
454 }
455 Ok(())
456 }
457
458 /// Cleans all values and removes null inner values at any depth.
459 /// if the replacement can not happen.
460 ///
461 /// ```
462 /// # use platform_value::{Error, Identifier, IntegerReplacementType, ReplacementType, Value};
463 /// #
464 /// let mut inner_value = Value::Map(
465 /// vec![
466 /// (Value::Text(String::from("grapes")), Value::Null),
467 /// (Value::Text(String::from("oranges")), Value::I32(6)),
468 /// ]
469 /// );
470 /// let mut value = Value::Map(
471 /// vec![
472 /// (Value::Text(String::from("foods")), inner_value),
473 /// ]
474 /// );
475 ///
476 /// value = value.clean_recursive().unwrap();
477 ///
478 /// assert_eq!(value.get_optional_value_at_path("foods.grapes"), Ok(None));
479 ///
480 pub fn clean_recursive(self) -> Result<Value, Error> {
481 Ok(Value::Map(
482 self.into_map()?
483 .into_iter()
484 .filter_map(|(key, value)| {
485 if value.is_null() {
486 None
487 } else if value.is_map() {
488 match value.clean_recursive() {
489 Ok(value) => Some(Ok((key, value))),
490 Err(e) => Some(Err(e)),
491 }
492 } else {
493 Some(Ok((key, value)))
494 }
495 })
496 .collect::<Result<Vec<_>, Error>>()?,
497 ))
498 }
499}