Skip to content

Commit

Permalink
Add support for keyed recursive descendant search
Browse files Browse the repository at this point in the history
Update README

Bump version
  • Loading branch information
mitghi committed Jul 15, 2023
1 parent 8c0dd1a commit baa85e8
Show file tree
Hide file tree
Showing 6 changed files with 185 additions and 53 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "jetro"
version = "0.2.2"
version = "0.2.3"
edition = "2021"
authors = ["Milad (Mike) Taghavi <mitghi.at.gmail.com>"]
license = "MIT"
Expand Down
45 changes: 40 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ Get value associated with `line_items`.
"name": "chilli",
"price": 0,
"total": 1,
"type": "ingridient"
"type": "ingredient"
},
{
"ident": "ewq",
Expand Down Expand Up @@ -207,6 +207,41 @@ Get value associated with first matching key which has a value and return its `i
```
</details>

---

Recursively search for objects that has key with specified value.

```
>/..('type'='ingredient')
```

<details>
<summary>See output</summary>

### result

```json
[
{
"ident": "ghi",
"is_gratis": false,
"name": "cheese",
"price": 2,
"total": 1,
"type": "ingredient"
},
{
"ident": "def",
"is_gratis": false,
"name": "salami",
"price": 2.8,
"total": 10,
"type": "ingredient"
}
]
```
</details>

---
```
>/..items/#tail
Expand Down Expand Up @@ -241,7 +276,7 @@ Get value associated with first matching key which has a value and return its `i
"name": "chilli",
"price": 0,
"total": 1,
"type": "ingridient"
"type": "ingredient"
},
{
"ident": "ewq",
Expand Down Expand Up @@ -274,7 +309,7 @@ Get value associated with first matching key which has a value and return its `i
"name": "chilli",
"price": 0,
"total": 1,
"type": "ingridient"
"type": "ingredient"
}
]
```
Expand All @@ -293,7 +328,7 @@ Get value associated with first matching key which has a value and return its `i

```json
[
"ingridient"
"ingredient"
]
```
</details>
Expand Down Expand Up @@ -374,7 +409,7 @@ Select up to 4 items from index zero of array `items`
"name": "chilli",
"price": 0,
"total": 1,
"type": "ingridient"
"type": "ingredient"
}
]
```
Expand Down
112 changes: 85 additions & 27 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,12 @@ pub struct FilterAST {
pub right: Option<Rc<RefCell<FilterAST>>>,
}

#[derive(Debug, PartialEq, Clone)]
pub enum FilterDescendant {
Single(String),
Pair(String, String),
}

/// Filter contains operations that transform, match
/// or search the input based on structure generated
/// the from parser.
Expand All @@ -219,7 +225,7 @@ pub enum Filter {
Root,
AnyChild,
Child(String),
Descendant(String),
DescendantChild(FilterDescendant),
Pick(Vec<PickFilterInner>),
ArrayIndex(usize),
ArrayFrom(usize),
Expand Down Expand Up @@ -668,9 +674,14 @@ impl Filter {

pub fn key(&self) -> Option<String> {
match &self {
Filter::Descendant(ref descendant) => {
return Some(descendant[2..].to_string());
}
Filter::DescendantChild(entry) => match entry {
FilterDescendant::Single(ref descendant) => {
return Some(descendant[2..].to_string());
}
_ => {
todo!("pair descendant not implemented");
}
},
_ => None,
}
}
Expand Down Expand Up @@ -1067,36 +1078,83 @@ impl<'a> Context<'a> {
_ => {}
},

(Filter::Descendant(ref descendant), Some(tail)) => match *current.value {
Value::Object(ref obj) => {
for (k, v) in obj {
let filters: &[Filter] = if k == descendant {
tail
} else {
current.filters
};

if filters.len() == 0 {
self.results.borrow_mut().push(Rc::new(v.clone()));
} else {
(Filter::DescendantChild(ref entry), Some(tail)) => match entry {
FilterDescendant::Single(ref descendant) => match *current.value {
Value::Object(ref obj) => {
for (k, v) in obj {
let filters: &[Filter] = if k == descendant {
tail
} else {
current.filters
};

if filters.len() == 0 {
self.results.borrow_mut().push(Rc::new(v.clone()));
} else {
self.stack.borrow_mut().push(StackItem::new(
Rc::new(v.clone()),
filters,
self.stack.clone(),
));
}
}
}
Value::Array(ref array) => {
for v in array {
self.stack.borrow_mut().push(StackItem::new(
Rc::new(v.clone()),
filters,
current.filters,
self.stack.clone(),
));
}
}
}
Value::Array(ref array) => {
for v in array {
self.stack.borrow_mut().push(StackItem::new(
Rc::new(v.clone()),
current.filters,
self.stack.clone(),
));
_ => {}
},
FilterDescendant::Pair(ref descendant, ref target_value) => {
match *current.value {
Value::Object(ref obj) => {
let mut found_match = false;
for (k, v) in obj {
let filters: &[Filter] = if k == descendant {
match &v {
Value::String(ref current_value) => {
if *current_value == *target_value {
found_match = true;
tail
} else {
current.filters
}
}
_ => current.filters,
}
} else {
current.filters
};

if filters.len() == 0 && found_match {
self.results
.borrow_mut()
.insert(0, current.value.clone());
}
self.stack.borrow_mut().push(StackItem::new(
Rc::new(v.clone()),
filters,
self.stack.clone(),
));
}
}
Value::Array(ref array) => {
for v in array {
self.stack.borrow_mut().push(StackItem::new(
Rc::new(v.clone()),
current.filters,
self.stack.clone(),
));
}
}
_ => {}
}
}
_ => {}
},

_ => {}
Expand Down Expand Up @@ -1177,7 +1235,7 @@ mod test {
let filters = vec![
Filter::Root,
Filter::Child("foo".to_string()),
Filter::Descendant("within".to_string()),
Filter::DescendantChild(FilterDescendant::Single("within".to_string())),
];

let mut ctx = Context::new(v, &filters);
Expand Down
6 changes: 4 additions & 2 deletions src/grammar.pest
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ or = { "or" }
logical_cmp = { and | or }
child = { slash ~ ident }
any_child = { slash ~ asterisk }
descendant_child = { slash ~ double_dot ~ ident }
keyed_ident = { literal ~ whitespace ~ "=" ~ whitespace ~ literal }
ident_or_keyed = { ( lparen ~ keyed_ident ~ rparen )| ident }
descendant_child = { slash ~ double_dot ~ ident_or_keyed }
grouped_any = { slash ~ grouped_literal }
array_index = { slash ~ "[" ~ number ~ "]" }
pure_index = {"[" ~ number ~ "]"}
Expand Down Expand Up @@ -82,4 +84,4 @@ expression = {
(path|reverse_path) ~ whitespace ~
(whitespace ~ pickFn ~ whitespace | filterFn ~ whitespace| grouped_any ~ whitespace | child ~ whitespace | any_child ~ whitespace | descendant_child ~ whitespace | array_index ~ whitespace | slice ~ whitespace | array_to ~ whitespace | array_from ~ whitespace | fn ~ whitespace)* ~
EOI
}
}
Loading

0 comments on commit baa85e8

Please sign in to comment.