1use crate::Value;
2use thiserror::Error as ThisError;
3
4pub mod path;
5
6fn parse_index(key: &str) -> Option<usize> {
7 if key == "0" {
8 Some(0)
9 } else if key.starts_with('0') {
10 None
11 } else {
12 key.parse().ok()
13 }
14}
15
16pub fn get<'v, S: AsRef<str>>(value: &'v Value, path: &[S]) -> Option<&'v Value> {
18 let mut result = value;
19 for s in path {
20 let key = s.as_ref();
21 result = match result {
22 Value::Map(map) => map.get(key)?,
23 Value::Array(array) => {
24 let idx = parse_index(key)?;
25 array.get(idx)?
26 }
27 _ => return None,
28 };
29 }
30 Some(result)
31}
32
33pub fn get_mut<'v, S: AsRef<str>>(value: &'v mut Value, path: &[S]) -> Option<&'v mut Value> {
35 let mut result = value;
36 for s in path {
37 let key = s.as_ref();
38 result = match result {
39 Value::Map(map) => map.get_mut(key)?,
40 Value::Array(array) => {
41 let idx = parse_index(key)?;
42 array.get_mut(idx)?
43 }
44 _ => return None,
45 };
46 }
47 Some(result)
48}
49
50pub fn remove<S: AsRef<str>>(value: &mut Value, path: &[S]) -> Option<Value> {
52 if path.is_empty() {
53 return Some(std::mem::replace(value, Value::Null));
54 }
55
56 let parent_path = &path[..path.len() - 1];
57
58 let parent = get_mut(value, parent_path)?;
59 let key = path.last().expect("!path.is_empty()").as_ref();
60 match parent {
61 Value::Map(map) => map.swap_remove(key),
62 Value::Array(array) => {
63 let idx = parse_index(key)?;
64 if idx < array.len() {
65 Some(array.remove(idx))
66 } else {
67 None
68 }
69 }
70 _ => None,
71 }
72}
73
74#[derive(ThisError, Debug)]
75#[error("failed to insert")]
76pub struct InsertError;
77
78pub fn insert<S: AsRef<str>>(
80 value: &mut Value,
81 path: &[S],
82 insert: Value,
83) -> Result<Option<Value>, InsertError> {
84 if path.is_empty() {
85 return Ok(Some(std::mem::replace(value, insert)));
86 }
87
88 let parent_path = &path[..path.len() - 1];
89 let parent = get_mut(value, parent_path).ok_or(InsertError)?;
90 let key = path.last().expect("!path.is_empty()").as_ref();
91 match parent {
92 Value::Map(map) => Ok(map.insert(key.to_owned(), insert)),
93 Value::Array(array) => {
94 if key == "-" {
95 array.push(insert);
96 } else {
97 let idx = parse_index(key).ok_or(InsertError)?;
98 if idx > array.len() {
99 return Err(InsertError);
100 } else {
101 array.insert(idx, insert);
102 }
103 }
104 Ok(None)
105 }
106 _ => Err(InsertError),
107 }
108}