platform_value/
pointer.rs1use crate::{Value, ValueMapHelper};
2use std::mem;
3
4fn parse_index(s: &str) -> Option<usize> {
5 if s.starts_with('+') || (s.starts_with('0') && s.len() != 1) {
6 return None;
7 }
8 s.parse().ok()
9}
10
11impl Value {
12 pub fn pointer(&self, pointer: &str) -> Option<&Value> {
39 if pointer.is_empty() {
40 return Some(self);
41 }
42 if !pointer.starts_with('/') {
43 return None;
44 }
45 pointer
46 .split('/')
47 .skip(1)
48 .map(|x| x.replace("~1", "/").replace("~0", "~"))
49 .try_fold(self, |target, token| match target {
50 Value::Map(map) => map.get_optional_key(&token),
51 Value::Array(list) => parse_index(&token).and_then(|x| list.get(x)),
52 _ => None,
53 })
54 }
55
56 pub fn pointer_mut(&mut self, pointer: &str) -> Option<&mut Value> {
92 if pointer.is_empty() {
93 return Some(self);
94 }
95 if !pointer.starts_with('/') {
96 return None;
97 }
98 pointer
99 .split('/')
100 .skip(1)
101 .map(|x| x.replace("~1", "/").replace("~0", "~"))
102 .try_fold(self, |target, token| match target {
103 Value::Map(map) => map.get_optional_key_mut(&token),
104 Value::Array(list) => parse_index(&token).and_then(move |x| list.get_mut(x)),
105 _ => None,
106 })
107 }
108
109 pub fn take(&mut self) -> Value {
119 mem::replace(self, Value::Null)
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use crate::{platform_value, Value};
126
127 #[test]
132 fn parse_index_valid_number() {
133 assert_eq!(super::parse_index("0"), Some(0));
134 assert_eq!(super::parse_index("1"), Some(1));
135 assert_eq!(super::parse_index("42"), Some(42));
136 }
137
138 #[test]
139 fn parse_index_leading_plus_returns_none() {
140 assert_eq!(super::parse_index("+1"), None);
141 }
142
143 #[test]
144 fn parse_index_leading_zero_returns_none() {
145 assert_eq!(super::parse_index("01"), None);
146 assert_eq!(super::parse_index("007"), None);
147 }
148
149 #[test]
150 fn parse_index_single_zero_is_valid() {
151 assert_eq!(super::parse_index("0"), Some(0));
152 }
153
154 #[test]
155 fn parse_index_non_numeric_returns_none() {
156 assert_eq!(super::parse_index("abc"), None);
157 assert_eq!(super::parse_index(""), None);
158 }
159
160 #[test]
165 fn pointer_empty_string_returns_self() {
166 let data = platform_value!({"a": 1});
167 assert_eq!(data.pointer(""), Some(&data));
168 }
169
170 #[test]
171 fn pointer_no_leading_slash_returns_none() {
172 let data = platform_value!({"a": 1});
173 assert_eq!(data.pointer("a"), None);
174 assert_eq!(data.pointer("a/b"), None);
175 }
176
177 #[test]
178 fn pointer_simple_key_lookup() {
179 let data = platform_value!({"a": 1, "b": 2});
180 assert_eq!(data.pointer("/a"), Some(&platform_value!(1)));
181 assert_eq!(data.pointer("/b"), Some(&platform_value!(2)));
182 }
183
184 #[test]
185 fn pointer_nested_key_lookup() {
186 let data = platform_value!({"x": {"y": {"z": 42}}});
187 assert_eq!(data.pointer("/x/y/z"), Some(&platform_value!(42)));
188 }
189
190 #[test]
191 fn pointer_missing_path_returns_none() {
192 let data = platform_value!({"a": 1});
193 assert_eq!(data.pointer("/b"), None);
194 assert_eq!(data.pointer("/a/b/c"), None);
195 }
196
197 #[test]
198 fn pointer_array_index() {
199 let data = platform_value!({"arr": [10, 20, 30]});
200 assert_eq!(data.pointer("/arr/0"), Some(&platform_value!(10)));
201 assert_eq!(data.pointer("/arr/1"), Some(&platform_value!(20)));
202 assert_eq!(data.pointer("/arr/2"), Some(&platform_value!(30)));
203 }
204
205 #[test]
206 fn pointer_array_out_of_bounds_returns_none() {
207 let data = platform_value!({"arr": [10]});
208 assert_eq!(data.pointer("/arr/5"), None);
209 }
210
211 #[test]
212 fn pointer_tilde_escape_tilde_zero_becomes_tilde() {
213 let data = platform_value!({"a~b": 1});
215 assert_eq!(data.pointer("/a~0b"), Some(&platform_value!(1)));
216 }
217
218 #[test]
219 fn pointer_tilde_escape_tilde_one_becomes_slash() {
220 let data = platform_value!({"a/b": 1});
222 assert_eq!(data.pointer("/a~1b"), Some(&platform_value!(1)));
223 }
224
225 #[test]
226 fn pointer_combined_tilde_escapes() {
227 let data = platform_value!({"~/key": 99});
228 assert_eq!(data.pointer("/~0~1key"), Some(&platform_value!(99)));
230 }
231
232 #[test]
233 fn pointer_scalar_value_returns_none_for_child() {
234 let data = platform_value!(42);
235 assert_eq!(data.pointer("/anything"), None);
236 }
237
238 #[test]
239 fn pointer_nested_array_in_map() {
240 let data = platform_value!({
241 "x": {
242 "y": ["z", "zz"]
243 }
244 });
245 assert_eq!(data.pointer("/x/y/0"), Some(&platform_value!("z")));
246 assert_eq!(data.pointer("/x/y/1"), Some(&platform_value!("zz")));
247 }
248
249 #[test]
250 fn pointer_root_is_array() {
251 let data = platform_value!(["a", "b", "c"]);
252 assert_eq!(data.pointer("/0"), Some(&platform_value!("a")));
253 assert_eq!(data.pointer("/2"), Some(&platform_value!("c")));
254 }
255
256 #[test]
257 fn pointer_leading_zero_index_rejected() {
258 let data = platform_value!({"arr": [10, 20, 30]});
259 assert_eq!(data.pointer("/arr/01"), None);
261 }
262
263 #[test]
268 fn pointer_mut_empty_string_returns_self() {
269 let mut data = platform_value!({"a": 1});
270 let reference = data.pointer_mut("");
271 assert!(reference.is_some());
272 }
273
274 #[test]
275 fn pointer_mut_no_leading_slash_returns_none() {
276 let mut data = platform_value!({"a": 1});
277 assert!(data.pointer_mut("a").is_none());
278 }
279
280 #[test]
281 fn pointer_mut_modify_nested_value() {
282 let mut data = platform_value!({"x": {"y": 1}});
283 *data.pointer_mut("/x/y").unwrap() = platform_value!(99);
284 assert_eq!(data.pointer("/x/y"), Some(&platform_value!(99)));
285 }
286
287 #[test]
288 fn pointer_mut_modify_array_element() {
289 let mut data = platform_value!({"arr": [10, 20, 30]});
290 *data.pointer_mut("/arr/1").unwrap() = platform_value!(999);
291 assert_eq!(data.pointer("/arr/1"), Some(&platform_value!(999)));
292 }
293
294 #[test]
295 fn pointer_mut_missing_path_returns_none() {
296 let mut data = platform_value!({"a": 1});
297 assert!(data.pointer_mut("/nonexistent").is_none());
298 assert!(data.pointer_mut("/a/b/c").is_none());
299 }
300
301 #[test]
302 fn pointer_mut_tilde_escapes() {
303 let mut data = platform_value!({"a/b": 1, "c~d": 2});
304 *data.pointer_mut("/a~1b").unwrap() = platform_value!(10);
305 *data.pointer_mut("/c~0d").unwrap() = platform_value!(20);
306 assert_eq!(data.pointer("/a~1b"), Some(&platform_value!(10)));
307 assert_eq!(data.pointer("/c~0d"), Some(&platform_value!(20)));
308 }
309
310 #[test]
311 fn pointer_mut_scalar_returns_none() {
312 let mut data = platform_value!("hello");
313 assert!(data.pointer_mut("/anything").is_none());
314 }
315
316 #[test]
321 fn take_replaces_with_null() {
322 let mut data = platform_value!({"x": "y"});
323 let taken = data.pointer_mut("/x").unwrap().take();
324 assert_eq!(taken, platform_value!("y"));
325 assert_eq!(data.pointer("/x"), Some(&Value::Null));
326 }
327
328 #[test]
329 fn take_on_integer() {
330 let mut val: Value = platform_value!(42);
331 let taken = val.take();
332 assert_eq!(taken, platform_value!(42));
333 assert_eq!(val, Value::Null);
334 }
335
336 #[test]
337 fn take_on_null_returns_null() {
338 let mut val = Value::Null;
339 let taken = val.take();
340 assert_eq!(taken, Value::Null);
341 assert_eq!(val, Value::Null);
342 }
343
344 #[test]
345 fn take_on_array() {
346 let mut val = platform_value!([1, 2, 3]);
347 let taken = val.take();
348 assert_eq!(taken, platform_value!([1, 2, 3]));
349 assert_eq!(val, Value::Null);
350 }
351
352 #[test]
353 fn take_nested_via_pointer_mut() {
354 let mut data = platform_value!({"a": {"b": "deep"}});
355 let taken = data.pointer_mut("/a/b").map(Value::take).unwrap();
356 assert_eq!(taken, platform_value!("deep"));
357 assert_eq!(data.pointer("/a/b"), Some(&Value::Null));
358 }
359}