Skip to content

Commit 6c36ff6

Browse files
committed
introduce a parser
1 parent 85b8bfe commit 6c36ff6

File tree

8 files changed

+133
-17
lines changed

8 files changed

+133
-17
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ edition = "2018"
77
[dependencies]
88
itertools = "0.9.0"
99
json = "0.12.4"
10+
pest = "2.1.3"
11+
pest_derive = "2.1.0"
1012
serde = { version = "1.0", features = ["derive"] }
1113
serde_json = "1.0.57"
1214
serde_yaml = "0.8.13"

src/grammar.pest

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
selector = _{ SOI ~ jsonPath ~ EOI }
2+
3+
jsonPath = ${ rootSelector ~ matcher* }
4+
rootSelector = { "$" }
5+
6+
matcher = { dotChild }
7+
8+
dotChild = { ".*" }
9+
10+
WHITESPACE = _{ " " }

src/jsonpath.rs

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,20 @@
44
* SPDX-License-Identifier: BSD-2-Clause
55
*/
66

7-
use serde_json::Value;
7+
use crate::parser;
8+
use crate::path::Path;
89

910
#[derive(Debug)]
10-
pub enum SyntaxError {
11-
Message(String),
11+
pub struct SyntaxError {
12+
message: String,
1213
}
1314

14-
fn err(message: &str) -> Result<&dyn Path, SyntaxError> {
15-
Err(SyntaxError::Message(message.to_string()))
15+
impl std::fmt::Display for SyntaxError {
16+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
17+
write!(f, "{}", self.message)
18+
}
1619
}
1720

18-
pub fn parse(_selector: &str) -> Result<&dyn Path, SyntaxError> {
19-
err("not implemented")
20-
}
21-
22-
pub enum FindError {
23-
// no errors yet
24-
}
25-
26-
pub trait Path {
27-
fn find(&self, document: Value) -> Result<Vec<Value>, FindError>;
21+
pub fn parse(selector: &str) -> Result<Box<dyn Path>, SyntaxError> {
22+
parser::parse(selector).map_err(|m| SyntaxError { message: m })
2823
}

src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,11 @@
44
* SPDX-License-Identifier: BSD-2-Clause
55
*/
66

7+
extern crate pest;
8+
#[macro_use]
9+
extern crate pest_derive;
10+
711
pub mod jsonpath;
12+
mod matchers;
13+
mod parser;
14+
pub mod path;

src/matchers.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2020 VMware, Inc.
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
use serde_json::Value;
8+
9+
// Matcher maps a node to a list of nodes. If the input node is not matched by the matcher or
10+
// the matcher does not select any subnodes of the input node, then the result is empty.
11+
pub trait Matcher {
12+
fn select(&self, node: Value) -> Vec<Value>;
13+
}
14+
15+
pub struct RootSelector {}
16+
17+
impl Matcher for RootSelector {
18+
fn select(&self, node: Value) -> Vec<Value> {
19+
let mut results = Vec::new();
20+
results.push(node);
21+
results
22+
}
23+
}

src/parser.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2020 VMware, Inc.
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
use crate::matchers;
8+
use crate::path::{FindError, Path};
9+
use crate::pest::Parser;
10+
use serde_json::Value;
11+
12+
#[derive(Parser)]
13+
#[grammar = "grammar.pest"]
14+
struct PathParser;
15+
16+
struct SelectorPath<'a> {
17+
matchers: Vec<&'a dyn matchers::Matcher>,
18+
}
19+
20+
#[allow(clippy::single_match)]
21+
pub fn parse(selector: &str) -> Result<Box<dyn Path>, String> {
22+
let selector_rule = PathParser::parse(Rule::selector, selector)
23+
.map_err(|e| format!("{}", e))?
24+
.next()
25+
.unwrap();
26+
27+
let mut ms: Vec<&dyn matchers::Matcher> = Vec::new();
28+
for r in selector_rule.into_inner() {
29+
match r.as_rule() {
30+
Rule::rootSelector => {
31+
ms.push(&matchers::RootSelector {});
32+
}
33+
_ => println!("r={:?}", r),
34+
}
35+
}
36+
37+
Ok(Box::new(SelectorPath { matchers: ms }))
38+
}
39+
40+
impl Path for SelectorPath<'_> {
41+
fn find(&self, document: Value) -> Result<Vec<Value>, FindError> {
42+
let mut nodes = Vec::new();
43+
nodes.push(document);
44+
45+
// pass nodes through each matcher in turn
46+
for m in self.matchers.clone().into_iter() {
47+
let mut selected = Vec::new();
48+
for n in nodes.clone().into_iter() {
49+
for r in m.select(n).into_iter() {
50+
selected.push(r);
51+
}
52+
}
53+
nodes = selected;
54+
}
55+
56+
Ok(nodes)
57+
}
58+
}

src/path.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Copyright 2020 VMware, Inc.
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
use serde_json::Value;
8+
9+
pub enum FindError {
10+
// no errors yet
11+
}
12+
13+
pub trait Path {
14+
fn find(&self, document: Value) -> Result<Vec<Value>, FindError>;
15+
}

tests/cts.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ mod tests {
4343
let path = jsonpath::parse(&t.selector);
4444
assert!(
4545
path.is_ok(),
46-
"parse failed: {:?}",
46+
"parsing {} failed: {}",
47+
t.selector,
4748
path.err().expect("should be an error")
4849
);
4950

@@ -52,7 +53,12 @@ mod tests {
5253
p.find(as_json_value(&t.document).expect("invalid document"))
5354
{
5455
if result != as_json_value_array(&t.result).expect("invalid result") {
55-
assert!(false, "incorrect result")
56+
assert!(
57+
false,
58+
"incorrect result, expected: {:?}, got: {:?}",
59+
as_json_value_array(&t.result).unwrap(),
60+
result
61+
)
5662
}
5763
} else {
5864
assert!(false, "find failed") // should not happen

0 commit comments

Comments
 (0)