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}