Skip to content

Commit b3bfc8e

Browse files
committed
refactor: integrate deletion functionality into Queryable trait
- Move delete_by_path from separate trait to Queryable trait - Use Queried<usize> type alias instead of Result for consistency - Remove delete_single method in favor of unified delete_by_path - Simplify error handling by preserving original query errors
1 parent 2c9f18d commit b3bfc8e

File tree

1 file changed

+85
-113
lines changed

1 file changed

+85
-113
lines changed

src/query/queryable.rs

Lines changed: 85 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::parser::errors::JsonPathError;
22
use crate::parser::model::{JpQuery, Segment, Selector};
33
use crate::parser::{parse_json_path, Parsed};
4-
use crate::query::QueryPath;
4+
use crate::query::{QueryPath, Queried};
55
use crate::JsonPath;
66
use serde_json::Value;
77
use std::borrow::Cow;
@@ -139,6 +139,37 @@ where
139139
{
140140
None
141141
}
142+
143+
/// Deletes all elements matching the given JSONPath
144+
///
145+
/// # Arguments
146+
/// * `path` - JSONPath string specifying elements to delete
147+
///
148+
/// # Returns
149+
/// * `Ok(usize)` - Number of elements deleted
150+
/// * `Err(JsonPathError)` - If the path is invalid or deletion fails
151+
///
152+
/// # Examples
153+
/// ```
154+
/// use serde_json::json;
155+
/// use jsonpath_rust::JsonPath;
156+
/// use crate::jsonpath_rust::query::queryable::Queryable;
157+
///
158+
/// let mut data = json!({
159+
/// "users": [
160+
/// {"name": "Alice", "age": 30},
161+
/// {"name": "Bob", "age": 25},
162+
/// {"name": "Charlie", "age": 35}
163+
/// ]
164+
/// });
165+
///
166+
/// // Delete users older than 30
167+
/// let deleted = data.delete_by_path("$.users[?(@.age > 30)]").unwrap();
168+
/// assert_eq!(deleted, 1);
169+
/// ```
170+
fn delete_by_path(&mut self, _path: &str) -> Queried<usize> {
171+
Err(JsonPathError::InvalidJsonPath("Deletion not supported".to_string()))
172+
}
142173
}
143174

144175
impl Queryable for Value {
@@ -280,119 +311,35 @@ impl Queryable for Value {
280311
.ok()
281312
.and_then(|p| self.pointer_mut(p.as_str()))
282313
}
283-
}
284-
285-
fn convert_js_path(path: &str) -> Parsed<String> {
286-
let JpQuery { segments } = parse_json_path(path)?;
287-
288-
let mut path = String::new();
289-
for segment in segments {
290-
match segment {
291-
Segment::Selector(Selector::Name(name)) => {
292-
path.push_str(&format!("/{}", name.trim_matches(|c| c == '\'')));
293-
}
294-
Segment::Selector(Selector::Index(index)) => {
295-
path.push_str(&format!("/{}", index));
296-
}
297-
s => {
298-
return Err(JsonPathError::InvalidJsonPath(format!(
299-
"Invalid segment: {:?}",
300-
s
301-
)));
302-
}
303-
}
304-
}
305-
Ok(path)
306-
}
307-
308-
pub trait QueryableDeletable: Queryable {
309-
/// Deletes all elements matching the given JSONPath
310-
///
311-
/// # Arguments
312-
/// * `path` - JSONPath string specifying elements to delete
313-
///
314-
/// # Returns
315-
/// * `Ok(usize)` - Number of elements deleted
316-
/// * `Err(JsonPathError)` - If the path is invalid or deletion fails
317-
///
318-
/// # Examples
319-
/// ```
320-
/// use serde_json::json;
321-
/// use crate::jsonpath_rust::query::queryable::QueryableDeletable;
322-
/// use jsonpath_rust::JsonPath;
323-
///
324-
/// let mut data = json!({
325-
/// "users": [
326-
/// {"name": "Alice", "age": 30},
327-
/// {"name": "Bob", "age": 25},
328-
/// {"name": "Charlie", "age": 35}
329-
/// ]
330-
/// });
331-
///
332-
/// // Delete users older than 30
333-
/// let deleted = data.delete_by_path("$.users[?(@.age > 30)]").unwrap();
334-
/// assert_eq!(deleted, 1);
335-
/// ```
336-
fn delete_by_path(&mut self, path: &str) -> Result<usize, JsonPathError>;
337-
338-
/// Deletes a single element at the given path
339-
/// Returns true if an element was deleted, false otherwise
340-
fn delete_single(&mut self, path: &str) -> Result<bool, JsonPathError>;
341-
}
342314

343-
impl QueryableDeletable for Value {
344-
fn delete_by_path(&mut self, path: &str) -> Result<usize, JsonPathError> {
345-
346-
let matching_paths = self.query_only_path(path)
347-
.map_err(|_| JsonPathError::InvalidJsonPath("Failed to query path".to_string()))?;
348-
349-
if matching_paths.is_empty() {
350-
return Ok(0);
351-
}
352-
315+
fn delete_by_path(&mut self, path: &str) -> Queried<usize> {
353316
let mut deletions = Vec::new();
354-
for query_path in &matching_paths {
317+
for query_path in &self.query_only_path(path)? {
355318
if let Some(deletion_info) = parse_deletion_path(query_path)? {
356319
deletions.push(deletion_info);
357320
}
358321
}
359322

360323
// Sort deletions to handle array indices correctly (delete from end to start)
361-
deletions.sort_by(|a, b| {
362-
// First sort by path depth (deeper paths first)
363-
let depth_cmp = b.path_depth().cmp(&a.path_depth());
364-
if depth_cmp != std::cmp::Ordering::Equal {
365-
return depth_cmp;
366-
}
367-
368-
// Then by array index (higher indices first)
369-
match (a, b) {
370-
(DeletionInfo::ArrayIndex { index: idx_a, .. },
371-
DeletionInfo::ArrayIndex { index: idx_b, .. }) => {
372-
idx_b.cmp(idx_a)
324+
deletions.sort_by(|a, b|
325+
b.path_depth().cmp(&a.path_depth()).then_with(|| {
326+
match (a, b) {
327+
(
328+
DeletionInfo::ArrayIndex { index: idx_a, .. },
329+
DeletionInfo::ArrayIndex { index: idx_b, .. },
330+
) => idx_b.cmp(idx_a),
331+
_ => std::cmp::Ordering::Equal,
373332
}
374-
_ => std::cmp::Ordering::Equal
375-
}
376-
});
333+
})
334+
);
377335

378336
// Perform deletions
379-
let mut deleted_count = 0;
380-
for deletion in deletions {
381-
if execute_deletion(self, &deletion)? {
382-
deleted_count += 1;
383-
}
384-
}
337+
let deleted_count = deletions
338+
.iter()
339+
.try_fold(0, |c, d| execute_deletion(self, d).map(|deleted| if deleted { c + 1 } else { c }))?;
385340

386341
Ok(deleted_count)
387342
}
388-
389-
fn delete_single(&mut self, path: &str) -> Result<bool, JsonPathError> {
390-
if let Some(deletion_info) = parse_deletion_path(path)? {
391-
execute_deletion(self, &deletion_info)
392-
} else {
393-
Ok(false)
394-
}
395-
}
396343
}
397344

398345
#[derive(Debug, Clone)]
@@ -444,10 +391,11 @@ fn parse_deletion_path(query_path: &str) -> Result<Option<DeletionInfo>, JsonPat
444391
Segment::Selector(Selector::Index(index)) => {
445392
parent_path.push_str(&format!("/{}", index));
446393
}
447-
_ => {
448-
return Err(JsonPathError::InvalidJsonPath(
449-
"Unsupported segment type for deletion".to_string()
450-
));
394+
e => {
395+
return Err(JsonPathError::InvalidJsonPath(format!(
396+
"Unsupported segment to be deleted: {:?}",
397+
e
398+
)));
451399
}
452400
}
453401
} else {
@@ -465,10 +413,11 @@ fn parse_deletion_path(query_path: &str) -> Result<Option<DeletionInfo>, JsonPat
465413
index: *index as usize,
466414
}));
467415
}
468-
_ => {
469-
return Err(JsonPathError::InvalidJsonPath(
470-
"Unsupported final segment for deletion".to_string()
471-
));
416+
e => {
417+
return Err(JsonPathError::InvalidJsonPath(format!(
418+
"Unsupported segment to be deleted: {:?}",
419+
e
420+
)));
472421
}
473422
}
474423
}
@@ -477,7 +426,7 @@ fn parse_deletion_path(query_path: &str) -> Result<Option<DeletionInfo>, JsonPat
477426
Ok(None)
478427
}
479428

480-
fn execute_deletion(value: &mut Value, deletion: &DeletionInfo) -> Result<bool, JsonPathError> {
429+
fn execute_deletion(value: &mut Value, deletion: &DeletionInfo) -> Queried<bool> {
481430
match deletion {
482431
DeletionInfo::Root => {
483432
*value = Value::Null;
@@ -525,10 +474,33 @@ fn execute_deletion(value: &mut Value, deletion: &DeletionInfo) -> Result<bool,
525474
}
526475
}
527476

477+
fn convert_js_path(path: &str) -> Parsed<String> {
478+
let JpQuery { segments } = parse_json_path(path)?;
479+
480+
let mut path = String::new();
481+
for segment in segments {
482+
match segment {
483+
Segment::Selector(Selector::Name(name)) => {
484+
path.push_str(&format!("/{}", name.trim_matches(|c| c == '\'')));
485+
}
486+
Segment::Selector(Selector::Index(index)) => {
487+
path.push_str(&format!("/{}", index));
488+
}
489+
s => {
490+
return Err(JsonPathError::InvalidJsonPath(format!(
491+
"Invalid segment: {:?}",
492+
s
493+
)));
494+
}
495+
}
496+
}
497+
Ok(path)
498+
}
499+
528500
#[cfg(test)]
529501
mod tests {
530502
use crate::parser::Parsed;
531-
use crate::query::queryable::{convert_js_path, Queryable, QueryableDeletable};
503+
use crate::query::queryable::{convert_js_path, Queryable};
532504
use crate::query::Queried;
533505
use crate::JsonPath;
534506
use serde_json::{json, Value};
@@ -773,8 +745,8 @@ mod tests {
773745
"test": "value"
774746
});
775747

776-
let deleted = data.delete_single("$").unwrap();
777-
assert_eq!(deleted, true);
748+
let deleted = data.delete_by_path("$").unwrap();
749+
assert_eq!(deleted, 1);
778750
assert_eq!(data, Value::Null);
779751
}
780-
}
752+
}

0 commit comments

Comments
 (0)