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 let Some(open_bracket_pos) = text.rfind('[') else {
8 return Ok(None);
9 };
10
11 if !text.ends_with(']') {
13 return Ok(None);
14 }
15
16 let field_name = &text[..open_bracket_pos];
18
19 if field_name.is_empty() || !field_name.chars().all(|c| c.is_alphanumeric() || c == '_') {
21 return Ok(None);
22 }
23
24 let inside_brackets = &text[open_bracket_pos + 1..text.len() - 1];
26
27 if inside_brackets.is_empty() {
29 return Ok(Some((field_name, None)));
30 }
31
32 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 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 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 if number_part < array.len() {
206 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 if number_part < array.len() {
246 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 match number_part.cmp(&array.len()) {
345 std::cmp::Ordering::Less => {
346 current_value = array.get_mut(number_part).unwrap();
348 }
349 std::cmp::Ordering::Equal => {
350 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}