platform_value/
inner_value_at_path.rs

1use crate::value_map::ValueMapHelper;
2use crate::{error, Error, Value, ValueMap};
3use std::collections::BTreeMap;
4
5pub(crate) fn is_array_path(text: &str) -> Result<Option<(&str, Option<usize>)>, Error> {
6    // 1. Find the last '[' character.
7    let Some(open_bracket_pos) = text.rfind('[') else {
8        return Ok(None);
9    };
10
11    // 2. Check if `text` ends with ']'.
12    if !text.ends_with(']') {
13        return Ok(None);
14    }
15
16    // 3. Extract the portion before the '[' as the field name.
17    let field_name = &text[..open_bracket_pos];
18
19    // 4. Ensure the field name consists only of word characters
20    if field_name.is_empty() || !field_name.chars().all(|c| c.is_alphanumeric() || c == '_') {
21        return Ok(None);
22    }
23
24    // 5. Extract the portion inside the brackets.
25    let inside_brackets = &text[open_bracket_pos + 1..text.len() - 1];
26
27    // 6. If the inside is empty, there is no index number
28    if inside_brackets.is_empty() {
29        return Ok(Some((field_name, None)));
30    }
31
32    // 7. Otherwise, parse the inside as a number (usize).
33    let index = inside_brackets
34        .parse::<usize>()
35        .map_err(|_| Error::IntegerSizeError)?;
36
37    Ok(Some((field_name, Some(index))))
38}
39
40impl Value {
41    pub fn remove_value_at_path(&mut self, path: &str) -> Result<Value, Error> {
42        let mut split = path.split('.').peekable();
43        let mut current_value = self;
44        let mut last_path_component = None;
45        while let Some(path_component) = split.next() {
46            if split.peek().is_none() {
47                last_path_component = Some(path_component);
48            } else {
49                let map = current_value.to_map_mut()?;
50                current_value = map.get_optional_key_mut(path_component).ok_or_else(|| {
51                    Error::StructureError(format!(
52                        "unable to remove property {path_component} in {path}"
53                    ))
54                })?;
55            };
56        }
57        let Some(last_path_component) = last_path_component else {
58            return Err(Error::StructureError("path was empty".to_string()));
59        };
60        let map = current_value.as_map_mut_ref()?;
61        map.remove_key(last_path_component)
62    }
63
64    pub fn remove_optional_value_at_path(&mut self, path: &str) -> Result<Option<Value>, Error> {
65        let mut split = path.split('.').peekable();
66        let mut current_value = self;
67        let mut last_path_component = None;
68        while let Some(path_component) = split.next() {
69            if split.peek().is_none() {
70                last_path_component = Some(path_component);
71            } else {
72                let map = current_value.to_map_mut()?;
73                if let Some(maybe_value) = map.get_optional_key_mut(path_component) {
74                    current_value = maybe_value;
75                } else {
76                    return Ok(None);
77                }
78            };
79        }
80        let Some(last_path_component) = last_path_component else {
81            return Err(Error::StructureError("path was empty".to_string()));
82        };
83        let map = current_value.as_map_mut_ref()?;
84        Ok(map.remove_optional_key(last_path_component))
85    }
86
87    pub fn remove_values_matching_path(&mut self, path: &str) -> Result<Vec<Value>, Error> {
88        let mut split = path.split('.').peekable();
89        let mut current_values = vec![self];
90        let mut removed_values = vec![];
91        while let Some(path_component) = split.next() {
92            if let Some((string_part, number_part)) = is_array_path(path_component)? {
93                current_values = current_values
94                    .into_iter()
95                    .filter_map(|current_value| {
96                        if current_value.is_null() {
97                            return None;
98                        }
99                        let Some(map) = current_value.as_map_mut() else {
100                            return Some(Err(Error::StructureError(
101                                "value is not a map during removal".to_string(),
102                            )));
103                        };
104
105                        let array_value = map.get_optional_key_mut(string_part)?;
106
107                        if array_value.is_null() {
108                            return None;
109                        }
110                        let Some(array) = array_value.as_array_mut() else {
111                            return Some(Err(Error::StructureError(
112                                "value is not an array during removal".to_string(),
113                            )));
114                        };
115                        if let Some(number_part) = number_part {
116                            if array.len() < number_part {
117                                //this already exists
118                                Some(Ok(vec![array.get_mut(number_part).unwrap()]))
119                            } else {
120                                Some(Err(Error::StructureError(format!(
121                                    "element at position {number_part} in array does not exist"
122                                ))))
123                            }
124                        } else {
125                            // we are replacing all members in array
126                            Some(Ok(array.iter_mut().collect()))
127                        }
128                    })
129                    .collect::<Result<Vec<Vec<&mut Value>>, Error>>()?
130                    .into_iter()
131                    .flatten()
132                    .collect()
133            } else {
134                current_values = current_values
135                    .into_iter()
136                    .filter_map(|current_value| {
137                        if current_value.is_null() {
138                            return None;
139                        }
140
141                        let map = match current_value.as_map_mut_ref() {
142                            Ok(map) => map,
143                            Err(err) => return Some(Err(err)),
144                        };
145
146                        if split.peek().is_none() {
147                            if let Some(removed) = map.remove_optional_key(path_component) {
148                                removed_values.push(removed)
149                            }
150                            None
151                        } else {
152                            let new_value = map.get_optional_key_mut(path_component)?;
153                            Some(Ok(new_value))
154                        }
155                    })
156                    .collect::<Result<Vec<&mut Value>, Error>>()?;
157            }
158        }
159        Ok(removed_values)
160    }
161
162    pub fn remove_value_at_path_into<T: TryFrom<Value, Error = error::Error>>(
163        &mut self,
164        path: &str,
165    ) -> Result<T, Error> {
166        self.remove_value_at_path(path)?.try_into()
167    }
168
169    pub fn remove_value_at_path_as_bytes(&mut self, path: &str) -> Result<Vec<u8>, Error> {
170        self.remove_value_at_path(path)?.try_into()
171    }
172
173    pub fn remove_values_at_paths<'a>(
174        &'a mut self,
175        paths: Vec<&'a str>,
176    ) -> Result<BTreeMap<&'a str, Value>, Error> {
177        paths
178            .into_iter()
179            .map(|path| Ok((path, self.remove_value_at_path(path)?)))
180            .collect()
181    }
182
183    pub fn remove_values_matching_paths<'a>(
184        &'a mut self,
185        paths: Vec<&'a str>,
186    ) -> Result<BTreeMap<&'a str, Vec<Value>>, Error> {
187        paths
188            .into_iter()
189            .map(|path| Ok((path, self.remove_values_matching_path(path)?)))
190            .collect()
191    }
192
193    pub fn get_value_at_path<'a>(&'a self, path: &str) -> Result<&'a Value, Error> {
194        let split = path.split('.');
195        let mut current_value = self;
196        for path_component in split {
197            if let Some((string_part, number_part)) = is_array_path(path_component)? {
198                let map = current_value.to_map_ref()?;
199                let array_value = map.get_key(string_part)?;
200                let array = array_value.to_array_ref()?;
201                let Some(number_part) = number_part else {
202                    return Err(Error::Unsupported("getting values of more than 1 member of an array is currently not supported".to_string()));
203                };
204                // We are setting the value of just member of the array
205                if number_part < array.len() {
206                    //this already exists
207                    current_value = array.get(number_part).unwrap()
208                } else {
209                    return Err(Error::StructureError(
210                        format!("trying to get the value in an array at an index {} higher than current array length {}", number_part, array.len()),
211                    ));
212                }
213            } else {
214                let map = current_value.to_map_ref()?;
215                current_value = map.get_optional_key(path_component).ok_or_else(|| {
216                    Error::StructureError(format!(
217                        "unable to get property {path_component} in {path}"
218                    ))
219                })?;
220            }
221        }
222        Ok(current_value)
223    }
224
225    pub fn get_optional_value_at_path<'a>(
226        &'a self,
227        path: &'a str,
228    ) -> Result<Option<&'a Value>, Error> {
229        let split = path.split('.');
230        let mut current_value = self;
231        for path_component in split {
232            if let Some((string_part, number_part)) = is_array_path(path_component)? {
233                let map = current_value.to_map_ref()?;
234                let Some(array_value) = map.get_optional_key(string_part) else {
235                    return Ok(None);
236                };
237                let array = array_value.to_array_ref()?;
238                let Some(number_part) = number_part else {
239                    return Err(Error::Unsupported(
240                        "setting values of all members in an array is currently not supported"
241                            .to_string(),
242                    ));
243                };
244                // We are setting the value of just member of the array
245                if number_part < array.len() {
246                    //this already exists
247                    current_value = array.get(number_part).unwrap()
248                } else {
249                    return Ok(None);
250                }
251            } else {
252                let map = current_value.to_map_ref()?;
253                let Some(new_value) = map.get_optional_key(path_component) else {
254                    return Ok(None);
255                };
256                current_value = new_value;
257            }
258        }
259        Ok(Some(current_value))
260    }
261
262    pub fn get_mut_value_at_path<'a>(&'a mut self, path: &'a str) -> Result<&'a mut Value, Error> {
263        let split = path.split('.');
264        let mut current_value = self;
265        for path_component in split {
266            let map = current_value.to_map_mut()?;
267            current_value = map.get_optional_key_mut(path_component).ok_or_else(|| {
268                Error::StructureError(format!(
269                    "unable to get mut property {path_component} in {path}"
270                ))
271            })?;
272        }
273        Ok(current_value)
274    }
275
276    pub fn get_optional_mut_value_at_path<'a>(
277        &'a mut self,
278        path: &'a str,
279    ) -> Result<Option<&'a mut Value>, Error> {
280        let split = path.split('.');
281        let mut current_value = self;
282        for path_component in split {
283            let map = current_value.to_map_mut()?;
284            let Some(new_value) = map.get_optional_key_mut(path_component) else {
285                return Ok(None);
286            };
287            current_value = new_value;
288        }
289        Ok(Some(current_value))
290    }
291
292    pub fn get_integer_at_path<T>(&self, path: &str) -> Result<T, Error>
293    where
294        T: TryFrom<i128>
295            + TryFrom<u128>
296            + TryFrom<u64>
297            + TryFrom<i64>
298            + TryFrom<u32>
299            + TryFrom<i32>
300            + TryFrom<u16>
301            + TryFrom<i16>
302            + TryFrom<u8>
303            + TryFrom<i8>,
304    {
305        self.get_value_at_path(path)?.to_integer()
306    }
307
308    pub fn get_optional_integer_at_path<T>(&self, path: &str) -> Result<Option<T>, Error>
309    where
310        T: TryFrom<i128>
311            + TryFrom<u128>
312            + TryFrom<u64>
313            + TryFrom<i64>
314            + TryFrom<u32>
315            + TryFrom<i32>
316            + TryFrom<u16>
317            + TryFrom<i16>
318            + TryFrom<u8>
319            + TryFrom<i8>,
320    {
321        self.get_optional_value_at_path(path)?
322            .map(|value| value.to_integer())
323            .transpose()
324    }
325
326    pub fn set_value_at_full_path(&mut self, path: &str, value: Value) -> Result<(), Error> {
327        let mut split = path.split('.').peekable();
328        let mut current_value = self;
329        let mut last_path_component = None;
330        while let Some(path_component) = split.next() {
331            if split.peek().is_none() {
332                last_path_component = Some(path_component);
333            } else if let Some((string_part, number_part)) = is_array_path(path_component)? {
334                let map = current_value.to_map_mut()?;
335                let array_value = map.get_key_mut_or_insert(string_part, Value::Array(vec![]));
336                let array = array_value.to_array_mut()?;
337                let Some(number_part) = number_part else {
338                    return Err(Error::Unsupported(
339                        "setting values of all members in an array is currently not supported"
340                            .to_string(),
341                    ));
342                };
343                // We are setting the value of just member of the array
344                match number_part.cmp(&array.len()) {
345                    std::cmp::Ordering::Less => {
346                        //this already exists
347                        current_value = array.get_mut(number_part).unwrap();
348                    }
349                    std::cmp::Ordering::Equal => {
350                        //we should create a new map
351                        array.push(Value::Map(ValueMap::new()));
352                        current_value = array.get_mut(number_part).unwrap();
353                    }
354                    std::cmp::Ordering::Greater => {
355                        return Err(Error::StructureError(
356                            "trying to insert into an array path higher than current array length"
357                                .to_string(),
358                        ));
359                    }
360                }
361            } else {
362                let map = current_value.to_map_mut()?;
363                current_value =
364                    map.get_key_mut_or_insert(path_component, Value::Map(ValueMap::new()));
365            };
366        }
367        let Some(last_path_component) = last_path_component else {
368            return Err(Error::StructureError("path was empty".to_string()));
369        };
370        let map = current_value.to_map_mut()?;
371        Self::insert_in_map(map, last_path_component, value);
372        Ok(())
373    }
374
375    pub fn set_value_at_path(&mut self, path: &str, key: &str, value: Value) -> Result<(), Error> {
376        let map = self.get_mut_value_at_path(path)?.as_map_mut_ref()?;
377        Self::insert_in_map(map, key, value);
378        Ok(())
379    }
380}
381#[cfg(test)]
382mod tests {
383    use super::*;
384    use crate::platform_value;
385
386    #[test]
387    fn insert_with_parents() {
388        let mut document = platform_value!({
389            "root" :  {
390                "from" : {
391                    "id": "123",
392                    "message": "text_message",
393                },
394            }
395        });
396
397        document
398            .set_value_at_full_path("root.to.new_field", platform_value!("new_value"))
399            .expect("no errors");
400        document
401            .set_value_at_full_path("root.array[0].new_field", platform_value!("new_value"))
402            .expect("no errors");
403
404        assert_eq!(document["root"]["from"]["id"], platform_value!("123"));
405        assert_eq!(
406            document["root"]["from"]["message"],
407            platform_value!("text_message")
408        );
409        assert_eq!(
410            document["root"]["to"]["new_field"],
411            platform_value!("new_value")
412        );
413        assert_eq!(
414            document["root"]["array"][0]["new_field"],
415            platform_value!("new_value")
416        );
417    }
418
419    mod is_array_path {
420        use super::*;
421
422        #[test]
423        fn test_valid_no_index() {
424            let result = is_array_path("array[]");
425            assert!(result.is_ok());
426            let maybe_tuple = result.unwrap();
427            assert!(maybe_tuple.is_some());
428            let (field_name, index) = maybe_tuple.unwrap();
429            assert_eq!(field_name, "array");
430            assert_eq!(index, None);
431        }
432
433        #[test]
434        fn test_valid_with_index() {
435            let result = is_array_path("arr[123]");
436            assert!(result.is_ok());
437            let maybe_tuple = result.unwrap();
438            assert!(maybe_tuple.is_some());
439            let (field_name, index) = maybe_tuple.unwrap();
440            assert_eq!(field_name, "arr");
441            assert_eq!(index, Some(123));
442        }
443
444        #[test]
445        fn test_no_brackets() {
446            let result = is_array_path("no_brackets");
447            assert!(result.is_ok());
448            assert!(result.unwrap().is_none());
449        }
450
451        #[test]
452        fn test_missing_closing_bracket() {
453            let result = is_array_path("array[");
454            assert!(result.is_ok());
455            assert!(result.unwrap().is_none());
456        }
457
458        #[test]
459        fn test_non_alphanumeric_field() {
460            let result = is_array_path("arr-test[123]");
461            assert!(result.is_ok());
462            assert!(result.unwrap().is_none());
463        }
464
465        #[test]
466        fn test_empty_field_name() {
467            let result = is_array_path("[123]");
468            assert!(result.is_ok());
469            assert!(result.unwrap().is_none());
470        }
471
472        #[test]
473        fn test_non_numeric_index() {
474            let result = is_array_path("array[abc]");
475            assert!(result.is_err());
476            assert_eq!(result.unwrap_err(), Error::IntegerSizeError);
477        }
478
479        #[test]
480        fn test_empty_index() {
481            let result = is_array_path("array[]");
482            assert!(result.is_ok());
483            let maybe_tuple = result.unwrap();
484            assert!(maybe_tuple.is_some());
485            let (field_name, index) = maybe_tuple.unwrap();
486            assert_eq!(field_name, "array");
487            assert_eq!(index, None);
488        }
489    }
490}