1use crate::parser::errors::JsonPathError;
2use crate::parser::model::{JpQuery, Segment, Selector};
3use crate::parser::{parse_json_path, Parsed};
4use crate::query::{Queried, QueryPath};
5use crate::JsonPath;
6use serde_json::Value;
7use std::borrow::Cow;
8use std::fmt::Debug;
9
10pub trait Queryable
55where
56 Self: Default
57 + Clone
58 + Debug
59 + for<'a> From<&'a str>
60 + From<bool>
61 + From<i64>
62 + From<f64>
63 + From<Vec<Self>>
64 + From<String>
65 + PartialEq,
66{
67 fn get(&self, key: &str) -> Option<&Self>;
71
72 fn as_array(&self) -> Option<&Vec<Self>>;
73
74 fn as_object(&self) -> Option<Vec<(&str, &Self)>>;
75
76 fn as_str(&self) -> Option<&str>;
77
78 fn as_i64(&self) -> Option<i64>;
79 fn as_f64(&self) -> Option<f64>;
80 fn as_bool(&self) -> Option<bool>;
81
82 fn null() -> Self;
84
85 fn extension_custom(_name: &str, _args: Vec<Cow<Self>>) -> Self {
86 Self::null()
87 }
88
89 fn reference<T>(&self, _path: T) -> Option<&Self>
95 where
96 T: Into<QueryPath>,
97 {
98 None
99 }
100
101 fn reference_mut<T>(&mut self, _path: T) -> Option<&mut Self>
137 where
138 T: Into<QueryPath>,
139 {
140 None
141 }
142
143 fn delete_by_path(&mut self, _path: &str) -> Queried<usize> {
171 Err(JsonPathError::InvalidJsonPath(
172 "Deletion not supported".to_string(),
173 ))
174 }
175}
176
177impl Queryable for Value {
178 fn get(&self, key: &str) -> Option<&Self> {
179 let key = if key.starts_with("'") && key.ends_with("'") {
180 key.trim_matches(|c| c == '\'')
181 } else if key.starts_with('"') && key.ends_with('"') {
182 key.trim_matches(|c| c == '"')
183 } else {
184 key
185 };
186 self.get(key)
187 }
188
189 fn as_array(&self) -> Option<&Vec<Self>> {
190 self.as_array()
191 }
192
193 fn as_object(&self) -> Option<Vec<(&str, &Self)>> {
194 self.as_object()
195 .map(|v| v.into_iter().map(|(k, v)| (k.as_str(), v)).collect())
196 }
197
198 fn as_str(&self) -> Option<&str> {
199 self.as_str()
200 }
201
202 fn as_i64(&self) -> Option<i64> {
203 self.as_i64()
204 }
205
206 fn as_f64(&self) -> Option<f64> {
207 self.as_f64()
208 }
209
210 fn as_bool(&self) -> Option<bool> {
211 self.as_bool()
212 }
213
214 fn null() -> Self {
215 Value::Null
216 }
217
218 fn extension_custom(name: &str, args: Vec<Cow<Self>>) -> Self {
250 match name {
251 "in" => match args.as_slice() {
252 [lhs, rhs] => match rhs.as_array() {
253 Some(elements) => elements.iter().any(|item| item == lhs.as_ref()).into(),
254 None => Self::null(),
255 },
256 _ => Self::null(),
257 },
258 "nin" => match args.as_slice() {
259 [lhs, rhs] => match rhs.as_array() {
260 Some(elements) => (!elements.iter().any(|item| item == lhs.as_ref())).into(),
261 None => Self::null(),
262 },
263 _ => Self::null(),
264 },
265 "none_of" => match args.as_slice() {
266 [lhs, rhs] => match (lhs.as_array(), rhs.as_array()) {
267 (Some(lhs_arr), Some(rhs_arr)) => lhs_arr
268 .iter()
269 .all(|lhs| !rhs_arr.iter().any(|rhs| lhs == rhs))
270 .into(),
271 _ => Self::null(),
272 },
273 _ => Self::null(),
274 },
275 "any_of" => match args.as_slice() {
276 [lhs, rhs] => match (lhs.as_array(), rhs.as_array()) {
277 (Some(lhs_arr), Some(rhs_arr)) => lhs_arr
278 .iter()
279 .any(|lhs| rhs_arr.iter().any(|rhs| lhs == rhs))
280 .into(),
281 _ => Self::null(),
282 },
283 _ => Self::null(),
284 },
285 "subset_of" => match args.as_slice() {
286 [lhs, rhs] => match (lhs.as_array(), rhs.as_array()) {
287 (Some(lhs_arr), Some(rhs_arr)) => lhs_arr
288 .iter()
289 .all(|lhs| rhs_arr.iter().any(|rhs| lhs == rhs))
290 .into(),
291 _ => Self::null(),
292 },
293 _ => Self::null(),
294 },
295 _ => Self::null(),
296 }
297 }
298
299 fn reference<T>(&self, path: T) -> Option<&Self>
300 where
301 T: Into<QueryPath>,
302 {
303 convert_js_path(&path.into())
304 .ok()
305 .and_then(|p| self.pointer(p.as_str()))
306 }
307
308 fn reference_mut<T>(&mut self, path: T) -> Option<&mut Self>
309 where
310 T: Into<QueryPath>,
311 {
312 convert_js_path(&path.into())
313 .ok()
314 .and_then(|p| self.pointer_mut(p.as_str()))
315 }
316
317 fn delete_by_path(&mut self, path: &str) -> Queried<usize> {
318 let mut deletions = Vec::new();
319 for query_path in &self.query_only_path(path)? {
320 if let Some(deletion_info) = parse_deletion_path(query_path)? {
321 deletions.push(deletion_info);
322 }
323 }
324
325 deletions.sort_by(|a, b| {
327 b.path_depth()
328 .cmp(&a.path_depth())
329 .then_with(|| match (a, b) {
330 (
331 DeletionInfo::ArrayIndex { index: idx_a, .. },
332 DeletionInfo::ArrayIndex { index: idx_b, .. },
333 ) => idx_b.cmp(idx_a),
334 _ => std::cmp::Ordering::Equal,
335 })
336 });
337
338 let deleted_count = deletions.iter().try_fold(0, |c, d| {
340 execute_deletion(self, d).map(|deleted| if deleted { c + 1 } else { c })
341 })?;
342
343 Ok(deleted_count)
344 }
345}
346
347#[derive(Debug, Clone)]
348enum DeletionInfo {
349 ObjectField {
350 parent_path: String,
351 field_name: String,
352 },
353 ArrayIndex {
354 parent_path: String,
355 index: usize,
356 },
357 Root,
358}
359
360impl DeletionInfo {
361 fn path_depth(&self) -> usize {
362 match self {
363 DeletionInfo::Root => 0,
364 DeletionInfo::ObjectField { parent_path, .. }
365 | DeletionInfo::ArrayIndex { parent_path, .. } => parent_path.matches('/').count(),
366 }
367 }
368}
369
370fn parse_deletion_path(query_path: &str) -> Result<Option<DeletionInfo>, JsonPathError> {
371 if query_path == "$" {
372 return Ok(Some(DeletionInfo::Root));
373 }
374
375 let JpQuery { segments } = parse_json_path(query_path)?;
376
377 if segments.is_empty() {
378 return Ok(None);
379 }
380
381 let mut parent_path = String::new();
382 let mut segments_iter = segments.iter().peekable();
383
384 while let Some(segment) = segments_iter.next() {
385 if segments_iter.peek().is_some() {
386 match segment {
388 Segment::Selector(Selector::Name(name)) => {
389 parent_path.push_str(&format!("/{}", name.trim_matches(|c| c == '\'')));
390 }
391 Segment::Selector(Selector::Index(index)) => {
392 parent_path.push_str(&format!("/{}", index));
393 }
394 e => {
395 return Err(JsonPathError::InvalidJsonPath(format!(
396 "Unsupported segment to be deleted: {:?}",
397 e
398 )));
399 }
400 }
401 } else {
402 match segment {
403 Segment::Selector(Selector::Name(name)) => {
404 let field_name = name.trim_matches(|c| c == '\'').to_string();
405 return Ok(Some(DeletionInfo::ObjectField {
406 parent_path,
407 field_name,
408 }));
409 }
410 Segment::Selector(Selector::Index(index)) => {
411 return Ok(Some(DeletionInfo::ArrayIndex {
412 parent_path,
413 index: *index as usize,
414 }));
415 }
416 e => {
417 return Err(JsonPathError::InvalidJsonPath(format!(
418 "Unsupported segment to be deleted: {:?}",
419 e
420 )));
421 }
422 }
423 }
424 }
425
426 Ok(None)
427}
428
429fn execute_deletion(value: &mut Value, deletion: &DeletionInfo) -> Queried<bool> {
430 match deletion {
431 DeletionInfo::Root => {
432 *value = Value::Null;
433 Ok(true)
434 }
435 DeletionInfo::ObjectField {
436 parent_path,
437 field_name,
438 } => {
439 let parent = if parent_path.is_empty() {
440 value
441 } else {
442 value.pointer_mut(parent_path).ok_or_else(|| {
443 JsonPathError::InvalidJsonPath("Parent path not found".to_string())
444 })?
445 };
446
447 if let Some(obj) = parent.as_object_mut() {
448 Ok(obj.remove(field_name).is_some())
449 } else {
450 Err(JsonPathError::InvalidJsonPath(
451 "Parent is not an object".to_string(),
452 ))
453 }
454 }
455 DeletionInfo::ArrayIndex { parent_path, index } => {
456 let parent = if parent_path.is_empty() {
457 value
458 } else {
459 value.pointer_mut(parent_path).ok_or_else(|| {
460 JsonPathError::InvalidJsonPath("Parent path not found".to_string())
461 })?
462 };
463
464 if let Some(arr) = parent.as_array_mut() {
465 if *index < arr.len() {
466 arr.remove(*index);
467 Ok(true)
468 } else {
469 Ok(false) }
471 } else {
472 Err(JsonPathError::InvalidJsonPath(
473 "Parent is not an array".to_string(),
474 ))
475 }
476 }
477 }
478}
479
480fn convert_js_path(path: &str) -> Parsed<String> {
481 let JpQuery { segments } = parse_json_path(path)?;
482
483 let mut path = String::new();
484 for segment in segments {
485 match segment {
486 Segment::Selector(Selector::Name(name)) => {
487 path.push_str(&format!("/{}", name.trim_matches(|c| c == '\'')));
488 }
489 Segment::Selector(Selector::Index(index)) => {
490 path.push_str(&format!("/{}", index));
491 }
492 s => {
493 return Err(JsonPathError::InvalidJsonPath(format!(
494 "Invalid segment: {:?}",
495 s
496 )));
497 }
498 }
499 }
500 Ok(path)
501}
502
503#[cfg(test)]
504mod tests {
505 use crate::parser::Parsed;
506 use crate::query::queryable::{convert_js_path, Queryable};
507 use crate::query::Queried;
508 use crate::JsonPath;
509 use serde_json::{json, Value};
510
511 #[test]
512 fn in_smoke() -> Queried<()> {
513 let json = json!({
514 "elems": ["test", "t1", "t2"],
515 "list": ["test", "test2", "test3"],
516 });
517
518 let res = json.query("$.elems[?in(@, $.list)]")?;
519
520 assert_eq!(res, [&json!("test")]);
521
522 Ok(())
523 }
524 #[test]
525 fn nin_smoke() -> Queried<()> {
526 let json = json!({
527 "elems": ["test", "t1", "t2"],
528 "list": ["test", "test2", "test3"],
529 });
530
531 let res = json.query("$.elems[?nin(@, $.list)]")?;
532
533 assert_eq!(res, [&json!("t1"), &json!("t2")]);
534
535 Ok(())
536 }
537 #[test]
538 fn none_of_smoke() -> Queried<()> {
539 let json = json!({
540 "elems": [ ["t1", "_"], ["t2", "t5"], ["t4"]],
541 "list": ["t1","t2", "t3"],
542 });
543
544 let res = json.query("$.elems[?none_of(@, $.list)]")?;
545
546 assert_eq!(res, [&json!(["t4"])]);
547
548 Ok(())
549 }
550 #[test]
551 fn any_of_smoke() -> Queried<()> {
552 let json = json!({
553 "elems": [ ["t1", "_"], ["t4", "t5"], ["t4"]],
554 "list": ["t1","t2", "t3"],
555 });
556
557 let res = json.query("$.elems[?any_of(@, $.list)]")?;
558
559 assert_eq!(res, [&json!(["t1", "_"])]);
560
561 Ok(())
562 }
563 #[test]
564 fn subset_of_smoke() -> Queried<()> {
565 let json = json!({
566 "elems": [ ["t1", "t2"], ["t4", "t5"], ["t6"]],
567 "list": ["t1","t2", "t3"],
568 });
569
570 let res = json.query("$.elems[?subset_of(@, $.list)]")?;
571
572 assert_eq!(res, [&json!(["t1", "t2"])]);
573
574 Ok(())
575 }
576
577 #[test]
578 fn convert_paths() -> Parsed<()> {
579 let r = convert_js_path("$.a.b[2]")?;
580 assert_eq!(r, "/a/b/2");
581
582 Ok(())
583 }
584
585 #[test]
586 fn test_references() -> Parsed<()> {
587 let mut json = json!({
588 "a": {
589 "b": {
590 "c": 42
591 }
592 }
593 });
594
595 let r = convert_js_path("$.a.b.c")?;
596
597 if let Some(v) = json.pointer_mut(r.as_str()) {
598 *v = json!(43);
599 }
600
601 assert_eq!(
602 json,
603 json!({
604 "a": {
605 "b": {
606 "c": 43
607 }
608 }
609 })
610 );
611
612 Ok(())
613 }
614 #[test]
615 fn test_js_reference() -> Parsed<()> {
616 let mut json = json!({
617 "a": {
618 "b": {
619 "c": 42
620 }
621 }
622 });
623
624 if let Some(path) = json.query_only_path("$.a.b.c")?.first() {
625 if let Some(v) = json.reference_mut(path) {
626 *v = json!(43);
627 }
628
629 assert_eq!(
630 json,
631 json!({
632 "a": {
633 "b": {
634 "c": 43
635 }
636 }
637 })
638 );
639 } else {
640 panic!("no path found");
641 }
642
643 Ok(())
644 }
645 #[test]
646 fn test_delete_object_field() {
647 let mut data = json!({
648 "users": {
649 "alice": {"age": 30},
650 "bob": {"age": 25}
651 }
652 });
653
654 let deleted = data.delete_by_path("$.users.alice").unwrap();
655 assert_eq!(deleted, 1);
656
657 let expected = json!({
658 "users": {
659 "bob": {"age": 25}
660 }
661 });
662 assert_eq!(data, expected);
663 }
664
665 #[test]
666 fn test_delete_array_element() {
667 let mut data = json!({
668 "numbers": [1, 2, 3, 4, 5]
669 });
670
671 let deleted = data.delete_by_path("$.numbers[2]").unwrap();
672 assert_eq!(deleted, 1);
673
674 let expected = json!({
675 "numbers": [1, 2, 4, 5]
676 });
677 assert_eq!(data, expected);
678 }
679
680 #[test]
681 fn test_delete_multiple_elements() {
682 let mut data = json!({
683 "users": [
684 {"name": "Alice", "age": 30},
685 {"name": "Bob", "age": 25},
686 {"name": "Charlie", "age": 35},
687 {"name": "David", "age": 22}
688 ]
689 });
690
691 let deleted = data.delete_by_path("$.users[?(@.age > 24)]").unwrap();
693 assert_eq!(deleted, 3);
694
695 let expected = json!({
696 "users": [
697 {"name": "David", "age": 22}
698 ]
699 });
700 assert_eq!(data, expected);
701 }
702
703 #[test]
704 fn test_delete_nested_fields() {
705 let mut data = json!({
706 "company": {
707 "departments": {
708 "engineering": {"budget": 100000},
709 "marketing": {"budget": 50000},
710 "hr": {"budget": 30000}
711 }
712 }
713 });
714
715 let deleted = data
716 .delete_by_path("$.company.departments.marketing")
717 .unwrap();
718 assert_eq!(deleted, 1);
719
720 let expected = json!({
721 "company": {
722 "departments": {
723 "engineering": {"budget": 100000},
724 "hr": {"budget": 30000}
725 }
726 }
727 });
728 assert_eq!(data, expected);
729 }
730
731 #[test]
732 fn test_delete_nonexistent_path() {
733 let mut data = json!({
734 "test": "value"
735 });
736
737 let deleted = data.delete_by_path("$.nonexistent").unwrap();
738 assert_eq!(deleted, 0);
739
740 let expected = json!({
742 "test": "value"
743 });
744 assert_eq!(data, expected);
745 }
746
747 #[test]
748 fn test_delete_root() {
749 let mut data = json!({
750 "test": "value"
751 });
752
753 let deleted = data.delete_by_path("$").unwrap();
754 assert_eq!(deleted, 1);
755 assert_eq!(data, Value::Null);
756 }
757}