diff --git a/.github/workflows/quickstart.yml b/.github/workflows/quickstart.yml index e211202..3e5945e 100644 --- a/.github/workflows/quickstart.yml +++ b/.github/workflows/quickstart.yml @@ -1,8 +1,4 @@ # Based on https://github.com/actions-rs/meta/blob/master/recipes/quickstart.md -# -# While our "example" application has the platform-specific code, -# for simplicity we are compiling and testing everything on the Ubuntu environment only. -# For multi-OS testing see the `cross.yml` workflow. on: [push, pull_request] @@ -12,15 +8,20 @@ jobs: check: name: Check runs-on: ubuntu-latest + strategy: + matrix: + toolchain: [ stable ] steps: - name: Checkout sources uses: actions/checkout@v2 + with: + submodules: 'true' - name: Install stable toolchain uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: stable + toolchain: ${{ matrix.toolchain }} override: true - name: Run cargo check @@ -31,15 +32,20 @@ jobs: test: name: Test Suite runs-on: ubuntu-latest + strategy: + matrix: + toolchain: [ stable ] steps: - name: Checkout sources uses: actions/checkout@v2 + with: + submodules: 'true' - name: Install stable toolchain uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: stable + toolchain: ${{ matrix.toolchain }} override: true - name: Run cargo test @@ -53,11 +59,14 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v2 + with: + submodules: 'true' - name: Install stable toolchain uses: actions-rs/toolchain@v1 with: profile: minimal + # lint using only the latest stable toolchain: stable override: true components: rustfmt, clippy @@ -72,4 +81,4 @@ jobs: uses: actions-rs/cargo@v1 with: command: clippy - args: -- -D warnings \ No newline at end of file + args: -- -D warnings -A clippy::upper-case-acronyms diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..88f08b5 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "jsonpath-compliance-test-suite"] + path = jsonpath-compliance-test-suite + url = git@github.com:glyn/jsonpath-compliance-test-suite.git diff --git a/Cargo.toml b/Cargo.toml index 856f7e0..9c4be77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "jsonpath_reference_implementation" version = "0.0.1" authors = ["Glyn Normington "] -edition = "2018" +edition = "2021" [dependencies] itertools = "0.9.0" @@ -11,4 +11,4 @@ pest = "2.1.3" pest_derive = "2.1.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.57" -slyce = "0.3.0" +slyce = "0.3.1" diff --git a/README.md b/README.md index 56dc99a..9dfbe45 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ # JSON Path Reference Implementation -**WORK IN PROGRESS** +**STALLED** - See this [blog post](https://underlap.org/jsonpath-from-blog-post-to-rfc-in-17-years) for details. If you want to take this implementation forward, please feel free to fork it or contact @glyn. This Reference Implementation follows, and usually lags behind, the [internet draft](https://jsonpath-standard.github.io/internet-draft/). -See [cts.json](tests/cts.json) for the Compliance Test Suite and [grammar.pest](src/grammar.pest) for the Parsing Expression Grammar of the Reference Implementation. +See [grammar.pest](src/grammar.pest) for the Parsing Expression Grammar of the Reference Implementation. -See the [Developer Guide](./DEVELOPING.md) if you want to run the code against the test suite. See the [Contributor Guide](./CONTRIBUTING.md) if you'd like to submit changes. \ No newline at end of file +See the [Developer Guide](./DEVELOPING.md) if you want to run the code against the test suite. See the [Contributor Guide](./CONTRIBUTING.md) if you'd like to submit changes. + +The Compliance Test Suite is in its own [repository](https://github.com/jsonpath-standard/jsonpath-compliance-test-suite). diff --git a/jsonpath-compliance-test-suite b/jsonpath-compliance-test-suite new file mode 160000 index 0000000..267acbf --- /dev/null +++ b/jsonpath-compliance-test-suite @@ -0,0 +1 @@ +Subproject commit 267acbf1b0987d164e9b691c589c83d27498db97 diff --git a/src/ast.rs b/src/ast.rs index c2fd948..785c6c2 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -54,6 +54,9 @@ pub enum Selector { Union(Vec), DotName(String), DotWildcard, + DescendantDotName(String), + DescendantDotWildcard, + DescendantUnion(Vec), } #[derive(Debug)] @@ -63,10 +66,11 @@ pub enum UnionElement { Index(i64), } -type Iter<'a> = Box + 'a>; +// NodeList is an iterator over references to Values named after the Nodelist term in the spec. +type NodeList<'a> = Box + 'a>; impl Path { - pub fn find<'a>(&'a self, input: &'a Value) -> Iter<'a> { + pub fn find<'a>(&'a self, input: &'a Value) -> NodeList<'a> { match self { Path::Root => Box::new(std::iter::once(input)), Path::Sel(left, sel) => Box::new(left.find(input).flat_map(move |v| sel.find(v))), @@ -75,21 +79,49 @@ impl Path { } impl Selector { - pub fn find<'a>(&'a self, input: &'a Value) -> Iter<'a> { + pub fn find<'a>(&'a self, input: &'a Value) -> NodeList<'a> { match self { - Selector::Union(indices) => Box::new(indices.iter().flat_map(move |i| i.get(input))), + Selector::Union(indices) => Box::new(indices.iter().flat_map(move |i| i.find(input))), Selector::DotName(name) => Box::new(input.get(name).into_iter()), Selector::DotWildcard => match input { Value::Object(m) => Box::new(m.values()), Value::Array(a) => Box::new(a.iter()), _ => Box::new(std::iter::empty()), }, + Selector::DescendantDotName(name) => { + Self::traverse(input, move |n: &'a Value| Box::new(n.get(name).into_iter())) + } + Selector::DescendantDotWildcard => { + Self::traverse(input, move |n: &'a Value| Box::new(iter::once(n))) + } + Selector::DescendantUnion(indices) => Self::traverse(input, move |n: &'a Value| { + Box::new(indices.iter().flat_map(move |i| i.find(n))) + }), + } + } + + // traverse applies the given closure to all the descendants of the input value and + // returns a nodelist. + fn traverse<'a, F>(input: &'a Value, f: F) -> NodeList<'a> + where + F: Fn(&'a Value) -> NodeList<'a> + Copy + 'a, + { + match input { + Value::Object(m) => Box::new( + m.into_iter() + .flat_map(move |(_k, v)| f(v).chain(Self::traverse::<'a>(v, f))), + ), + Value::Array(a) => Box::new( + a.iter() + .flat_map(move |v| f(v).chain(Self::traverse::<'a>(v, f))), + ), + _ => Box::new(std::iter::empty()), } } } impl UnionElement { - pub fn get<'a>(&self, v: &'a Value) -> Iter<'a> { + pub fn find<'a>(&self, v: &'a Value) -> NodeList<'a> { match self { UnionElement::Name(name) => Box::new(v.get(name).into_iter()), UnionElement::Slice(slice) => { diff --git a/src/grammar.pest b/src/grammar.pest index 51f7638..64fc5f9 100644 --- a/src/grammar.pest +++ b/src/grammar.pest @@ -1,9 +1,9 @@ -selector = _{ SOI ~ rootSelector ~ matchers ~ EOI } +selector = ${ SOI ~ rootSelector ~ matchers ~ EOI } matchers = ${ matcher* } -rootSelector = { "$" } +rootSelector = @{ "$" } -matcher = !{ dotChild | union } +matcher = !{ dotChild | union | wildcardedIndex | descendant } dotChild = _{ wildcardedDotChild | namedDotChild } wildcardedDotChild = { ".*" } @@ -17,7 +17,7 @@ char = { | '\u{80}'..'\u{10FFFF}' } -union = { "[" ~ unionElement ~ ("," ~ unionElement)* ~ "]" } +union = !{ "[" ~ unionElement ~ ("," ~ unionElement)* ~ "]" } unionElement = _{ unionChild | unionArraySlice | unionArrayIndex } unionChild = ${ doubleQuotedString | singleQuotedString } unionArrayIndex = @{ integer } @@ -27,21 +27,27 @@ sliceStart = @{ integer } sliceEnd = @{ integer } sliceStep = @{ integer } +wildcardedIndex = { "[" ~ "*" ~ "]" } + +descendant = ${ ".." ~ descendantVariant } +descendantVariant = _{ childName | wildcard | "[" ~ wildcard ~ "]" | union } +wildcard = { "*" } + doubleQuotedString = _{ "\"" ~ doubleInner ~ "\"" } doubleInner = @{ doubleChar* } doubleChar = { !("\"" | "\\" | '\u{00}'..'\u{1F}') ~ ANY | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t") - | "\\" ~ ("u" ~ upperHexDigit{4}) + | "\\" ~ ("u" ~ hexDigit{4}) } -upperHexDigit = _{ ASCII_DIGIT | "A" | "B" | "C" | "D" | "E" | "F" } +hexDigit = _{ ASCII_DIGIT | "A" | "B" | "C" | "D" | "E" | "F" | "a" | "b" | "c" | "d" | "e" | "f" } singleQuotedString = _{ "'" ~ singleInner ~ "'" } singleInner = @{ singleChar* } singleChar = { !("'" | "\\" | '\u{00}'..'\u{1F}') ~ ANY | "\\" ~ ("'" | "\\" | "/" | "b" | "f" | "n" | "r" | "t") - | "\\" ~ ("u" ~ upperHexDigit{4}) + | "\\" ~ ("u" ~ hexDigit{4}) } WHITESPACE = _{ " " } \ No newline at end of file diff --git a/src/parser.rs b/src/parser.rs index 57d3302..3fb6855 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -16,10 +16,13 @@ struct PathParser; pub fn parse(selector: &str) -> Result { let selector_rule = PathParser::parse(Rule::selector, selector) .map_err(|e| format!("{}", e))? - .nth(1) + .next() .unwrap(); selector_rule + .into_inner() + .nth(1) // skip over Rule::rootSelector + .unwrap() .into_inner() .fold(Ok(Path::Root), |prev, r| match r.as_rule() { Rule::matcher => Ok(Path::Sel( @@ -37,6 +40,8 @@ fn parse_selector(matcher_rule: pest::iterators::Pair) -> Result Selector::DotWildcard, Rule::namedDotChild => Selector::DotName(parse_child_name(r)), Rule::union => Selector::Union(parse_union_indices(r)?), + Rule::wildcardedIndex => Selector::DotWildcard, + Rule::descendant => parse_descendant(r)?, _ => panic!("invalid parse tree {:?}", r), }) } @@ -112,6 +117,16 @@ fn parse_union_array_slice( })) } +fn parse_descendant(matcher_rule: pest::iterators::Pair) -> Result { + let r = matcher_rule.into_inner().next().unwrap(); + + Ok(match r.as_rule() { + Rule::childName => Selector::DescendantDotName(r.as_str().to_owned()), + Rule::wildcard => Selector::DescendantDotWildcard, + _ => Selector::DescendantUnion(parse_union_indices(r)?), + }) +} + fn unescape(contents: &str) -> String { let s = format!(r#""{}""#, contents); serde_json::from_str(&s).unwrap() diff --git a/tests/cts.json b/tests/cts.json deleted file mode 100644 index b2a796a..0000000 --- a/tests/cts.json +++ /dev/null @@ -1,748 +0,0 @@ -{ "tests": [ - { - "name": "root", - "selector": "$", - "document": ["first", "second"], - "result": [["first", "second"]] - }, { - "name": "dot child", - "selector": "$.a", - "document": {"a" : "A", "b" : "B"}, - "result": ["A"] - }, { - "name": "dot child, ☺ name", - "selector": "$.☺", - "document": {"☺" : "A", "b" : "B"}, - "result": ["A"] - }, { - "name": "dot child, invalid character", - "selector": "$.&", - "invalid_selector": true - }, { - "name": "dot child absent", - "selector": "$.c", - "document": {"a" : "A", "b" : "B"}, - "result": [] - }, { - "name": "dot child of array", - "selector": "$.a", - "document": ["first", "second"], - "result": [] - }, { - "name": "wildcarded child", - "selector": "$.*", - "document": {"a" : "A", "b" : "B"}, - "result": ["A", "B"] - }, { - "name": "wildcarded child of array", - "selector": "$.*", - "document": ["first", "second"], - "result": ["first", "second"] - }, { - "name": "wildcarded child dot child", - "selector": "$.*.a", - "document": {"x": {"a" : "Ax", "b" : "Bx"}, "y": {"a" : "Ay", "b" : "By"}}, - "result": ["Ax", "Ay"] - }, { - "name": "union child, double quotes", - "selector": "$[\"a\"]", - "document": {"a" : "A", "b" : "B"}, - "result": ["A"] - }, { - "name": "union child absent, double quotes", - "selector": "$[\"c\"]", - "document": {"a" : "A", "b" : "B"}, - "result": [] - }, { - "name": "union child of array, double quotes", - "selector": "$[\"a\"]", - "document": ["first", "second"], - "result": [] - }, { - "name": "union child, double quotes, embedded U+0000", - "selector": "$[\"\u0000\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+0001", - "selector": "$[\"\u0001\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+0002", - "selector": "$[\"\u0002\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+0003", - "selector": "$[\"\u0003\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+0004", - "selector": "$[\"\u0004\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+0005", - "selector": "$[\"\u0005\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+0006", - "selector": "$[\"\u0006\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+0007", - "selector": "$[\"\u0007\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+0008", - "selector": "$[\"\u0008\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+0009", - "selector": "$[\"\u0009\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+000A", - "selector": "$[\"\u000A\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+000B", - "selector": "$[\"\u000B\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+000C", - "selector": "$[\"\u000C\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+000D", - "selector": "$[\"\u000D\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+000E", - "selector": "$[\"\u000E\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+000F", - "selector": "$[\"\u000F\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+0010", - "selector": "$[\"\u0010\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+0011", - "selector": "$[\"\u0011\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+0012", - "selector": "$[\"\u0012\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+0013", - "selector": "$[\"\u0013\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+0014", - "selector": "$[\"\u0014\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+0015", - "selector": "$[\"\u0015\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+0016", - "selector": "$[\"\u0016\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+0017", - "selector": "$[\"\u0017\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+0018", - "selector": "$[\"\u0018\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+0019", - "selector": "$[\"\u0019\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+001A", - "selector": "$[\"\u001A\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+001B", - "selector": "$[\"\u001B\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+001C", - "selector": "$[\"\u001C\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+001D", - "selector": "$[\"\u001D\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+001E", - "selector": "$[\"\u001E\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+001F", - "selector": "$[\"\u001F\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded U+0020", - "selector": "$[\"\u0020\"]", - "document": {"\u0020" : "A"}, - "result": ["A"] - }, { - "name": "union child, double quotes, escaped double quote", - "selector": "$[\"\\\"\"]", - "document": {"\"" : "A"}, - "result": ["A"] - }, { - "name": "union child, double quotes, escaped reverse solidus", - "selector": "$[\"\\\\\"]", - "document": {"\\" : "A"}, - "result": ["A"] - }, { - "name": "union child, double quotes, escaped solidus", - "selector": "$[\"\\/\"]", - "document": {"/" : "A"}, - "result": ["A"] - }, { - "name": "union child, double quotes, escaped backspace", - "selector": "$[\"\\b\"]", - "document": {"\u0008" : "A"}, - "result": ["A"] - }, { - "name": "union child, double quotes, escaped form feed", - "selector": "$[\"\\f\"]", - "document": {"\u000C" : "A"}, - "result": ["A"] - }, { - "name": "union child, double quotes, escaped line feed", - "selector": "$[\"\\n\"]", - "document": {"\u000A" : "A"}, - "result": ["A"] - }, { - "name": "union child, double quotes, escaped carriage return", - "selector": "$[\"\\r\"]", - "document": {"\u000D" : "A"}, - "result": ["A"] - }, { - "name": "union child, double quotes, escaped tab", - "selector": "$[\"\\t\"]", - "document": {"\u0009" : "A"}, - "result": ["A"] - }, { - "name": "union child, double quotes, escaped ☺", - "selector": "$[\"\\u263A\"]", - "document": {"☺" : "A"}, - "result": ["A"] - }, { - "name": "union child, double quotes, surrogate pair 𝄞", - "selector": "$[\"\\uD834\\uDD1E\"]", - "document": {"𝄞" : "A"}, - "result": ["A"] - }, { - "name": "union child, double quotes, surrogate pair 😀", - "selector": "$[\"\\uD83D\\uDE00\"]", - "document": {"😀" : "A"}, - "result": ["A"] - }, { - "name": "union child, double quotes, invalid escaped ☺", - "selector": "$[\"\\u263a\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, invalid escaped single quote", - "selector": "$[\"\\'\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, embedded double quote", - "selector": "$[\"\"\"]", - "invalid_selector": true - }, { - "name": "union child, double quotes, incomplete escape", - "selector": "$[\"\\\"]", - "invalid_selector": true - }, { - "name": "union child, single quotes", - "selector": "$['a']", - "document": {"a" : "A", "b" : "B"}, - "result": ["A"] - }, { - "name": "union child absent, single quotes", - "selector": "$['c']", - "document": {"a" : "A", "b" : "B"}, - "result": [] - }, { - "name": "union child of array, single quotes", - "selector": "$['a']", - "document": ["first", "second"], - "result": [] - }, { - "name": "union child, single quotes, embedded U+0000", - "selector": "$['\u0000']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+0001", - "selector": "$['\u0001']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+0002", - "selector": "$['\u0002']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+0003", - "selector": "$['\u0003']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+0004", - "selector": "$['\u0004']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+0005", - "selector": "$['\u0005']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+0006", - "selector": "$['\u0006']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+0007", - "selector": "$['\u0007']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+0008", - "selector": "$['\u0008']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+0009", - "selector": "$['\u0009']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+000A", - "selector": "$['\u000A']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+000B", - "selector": "$['\u000B']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+000C", - "selector": "$['\u000C']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+000D", - "selector": "$['\u000D']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+000E", - "selector": "$['\u000E']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+000F", - "selector": "$['\u000F']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+0010", - "selector": "$['\u0010']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+0011", - "selector": "$['\u0011']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+0012", - "selector": "$['\u0012']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+0013", - "selector": "$['\u0013']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+0014", - "selector": "$['\u0014']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+0015", - "selector": "$['\u0015']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+0016", - "selector": "$['\u0016']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+0017", - "selector": "$['\u0017']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+0018", - "selector": "$['\u0018']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+0019", - "selector": "$['\u0019']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+001A", - "selector": "$['\u001A']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+001B", - "selector": "$['\u001B']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+001C", - "selector": "$['\u001C']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+001D", - "selector": "$['\u001D']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+001E", - "selector": "$['\u001E']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+001F", - "selector": "$['\u001F']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded U+0020", - "selector": "$['\u0020']", - "document": {"\u0020" : "A"}, - "result": ["A"] - }, { - "name": "union child, single quotes, escaped single quote", - "selector": "$['\\'']", - "document": {"'" : "A"}, - "result": ["A"] - }, { - "name": "union child, single quotes, escaped reverse solidus", - "selector": "$['\\\\']", - "document": {"\\" : "A"}, - "result": ["A"] - }, { - "name": "union child, single quotes, escaped solidus", - "selector": "$['\\/']", - "document": {"/" : "A"}, - "result": ["A"] - }, { - "name": "union child, single quotes, escaped backspace", - "selector": "$['\\b']", - "document": {"\u0008" : "A"}, - "result": ["A"] - }, { - "name": "union child, single quotes, escaped form feed", - "selector": "$['\\f']", - "document": {"\u000C" : "A"}, - "result": ["A"] - }, { - "name": "union child, single quotes, escaped line feed", - "selector": "$['\\n']", - "document": {"\u000A" : "A"}, - "result": ["A"] - }, { - "name": "union child, single quotes, escaped carriage return", - "selector": "$['\\r']", - "document": {"\u000D" : "A"}, - "result": ["A"] - }, { - "name": "union child, single quotes, escaped tab", - "selector": "$['\\t']", - "document": {"\u0009" : "A"}, - "result": ["A"] - }, { - "name": "union child, single quotes, escaped ☺", - "selector": "$['\\u263A']", - "document": {"☺" : "A"}, - "result": ["A"] - }, { - "name": "union child, single quotes, surrogate pair 𝄞", - "selector": "$['\\uD834\\uDD1E']", - "document": {"𝄞" : "A"}, - "result": ["A"] - }, { - "name": "union child, single quotes, surrogate pair 😀", - "selector": "$['\\uD83D\\uDE00']", - "document": {"😀" : "A"}, - "result": ["A"] - }, { - "name": "union child, single quotes, invalid escaped ☺", - "selector": "$['\\u263a']", - "invalid_selector": true - }, { - "name": "union child, single quotes, invalid escaped double quote", - "selector": "$['\\\"']", - "invalid_selector": true - }, { - "name": "union child, single quotes, embedded single quote", - "selector": "$[''']", - "invalid_selector": true - }, { - "name": "union child, single quotes, incomplete escape", - "selector": "$['\\']", - "invalid_selector": true - }, { - "name": "union", - "selector": "$[0,2]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [0, 2] - }, { - "name": "union with whitespace", - "selector": "$[ 0 , 1 ]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [0, 1] - }, { - "name": "empty union", - "selector": "$[]", - "invalid_selector": true - }, { - "name": "union array access", - "selector": "$[0]", - "document": ["first", "second"], - "result": ["first"] - }, { - "name": "union array access, 1", - "selector": "$[1]", - "document": ["first", "second"], - "result": ["second"] - }, { - "name": "union array access, out of bound", - "selector": "$[2]", - "document": ["first", "second"], - "result": [] - }, { - "name": "union array access, overflowing index", - "selector": "$[231584178474632390847141970017375815706539969331281128078915168015826259279872]", - "invalid_selector": true - }, { - "name": "union array access, negative", - "selector": "$[-1]", - "document": ["first", "second"], - "result": ["second"] - }, { - "name": "union array access, more negative", - "selector": "$[-2]", - "document": ["first", "second"], - "result": ["first"] - }, { - "name": "union array access, negative out of bound", - "selector": "$[-3]", - "document": ["first", "second"], - "result": [] - }, { - "name": "union array access, on object", - "selector": "$[0]", - "document": {"foo": 1}, - "result": [] - }, { - "name": "union array access, leading 0", - "selector": "$[01]", - "invalid_selector": true - }, { - "name": "union array access, leading -0", - "selector": "$[-01]", - "invalid_selector": true - }, { - "name": "union array slice", - "selector": "$[1:3]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [1, 2] - }, { - "name": "union array slice with step", - "selector": "$[1:6:2]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [1, 3, 5] - }, { - "name": "union array slice with everything omitted, short form", - "selector": "$[:]", - "document": [0, 1, 2, 3], - "result": [0, 1, 2, 3] - }, { - "name": "union array slice with everything omitted, long form", - "selector": "$[::]", - "document": [0, 1, 2, 3], - "result": [0, 1, 2, 3] - }, { - "name": "union array slice with start omitted", - "selector": "$[:2]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [0, 1] - }, { - "name": "union array slice with start and end omitted", - "selector": "$[::2]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [0, 2, 4, 6, 8] - }, { - "name": "union array slice, last index", - "selector": "$[-1]", - "document": [0, 1, 2, 3], - "result": [3] - }, { - "name": "union array slice, overflowed index", - "selector": "$[4]", - "document": [0, 1, 2, 3], - "result": [] - }, { - "name": "union array slice, underflowed index", - "selector": "$[-5]", - "document": [0, 1, 2, 3], - "result": [] - }, { - "name": "union array slice, negative step with default start and end", - "selector": "$[::-1]", - "document": [0, 1, 2, 3], - "result": [3, 2, 1, 0] - }, { - "name": "union array slice, negative step with default start", - "selector": "$[:0:-1]", - "document": [0, 1, 2, 3], - "result": [3, 2, 1] - }, { - "name": "union array slice, negative step with default end", - "selector": "$[2::-1]", - "document": [0, 1, 2, 3], - "result": [2, 1, 0] - }, { - "name": "union array slice, larger negative step", - "selector": "$[::-2]", - "document": [0, 1, 2, 3], - "result": [3, 1] - }, { - "name": "union array slice, negative range with default step", - "selector": "$[-1:-3]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [] - }, { - "name": "union array slice, negative range with negative step", - "selector": "$[-1:-3:-1]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [9, 8] - }, { - "name": "union array slice, negative range with larger negative step", - "selector": "$[-1:-6:-2]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [9, 7, 5] - }, { - "name": "union array slice, larger negative range with larger negative step", - "selector": "$[-1:-7:-2]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [9, 7, 5] - }, { - "name": "union array slice, negative from, positive to", - "selector": "$[-5:7]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [5, 6] - }, { - "name": "union array slice, negative from", - "selector": "$[-2:]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [8, 9] - }, { - "name": "union array slice, positive from, negative to", - "selector": "$[1:-1]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [1, 2, 3, 4, 5, 6, 7, 8] - }, { - "name": "union array slice, negative from, positive to, negative step", - "selector": "$[-1:1:-1]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [9, 8, 7, 6, 5, 4, 3, 2] - }, { - "name": "union array slice, positive from, negative to, negative step", - "selector": "$[7:-5:-1]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [7, 6] - }, { - "name": "union array slice, too many colons", - "selector": "$[1:2:3:4]", - "invalid_selector": true - }, { - "name": "union array slice, non-integer array index", - "selector": "$[1:2:a]", - "invalid_selector": true - }, { - "name": "union array slice, zero step", - "selector": "$[1:2:0]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [] - }, { - "name": "union array slice, empty range", - "selector": "$[2:2]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [] - }, { - "name": "union array slice, default indices with empty array", - "selector": "$[:]", - "document": [], - "result": [] - }, { - "name": "union array slice, negative step with empty array", - "selector": "$[::-1]", - "document": [], - "result": [] - }, { - "name": "union array slice, maximal range with positive step", - "selector": "$[0:10]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - }, { - "name": "union array slice, maximal range with negative step", - "selector": "$[9:0:-1]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [9, 8, 7, 6, 5, 4, 3, 2, 1] - }, { - "name": "union array slice, excessively large to value", - "selector": "$[2:113667776004]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [2, 3, 4, 5, 6, 7, 8, 9] - }, { - "name": "union array slice, excessively small from value", - "selector": "$[-113667776004:1]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [0] - }, { - "name": "union array slice, excessively large from value with negative step", - "selector": "$[113667776004:0:-1]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [9, 8, 7, 6, 5, 4, 3, 2, 1] - }, { - "name": "union array slice, excessively small to value with negative step", - "selector": "$[3:-113667776004:-1]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [3, 2, 1, 0] - }, { - "name": "union array slice, excessively large step", - "selector": "$[1:10:113667776004]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [1] - }, { - "name": "union array slice, excessively small step", - "selector": "$[-1:-10:-113667776004]", - "document": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - "result": [9] - }, { - "name": "union array slice, overflowing to value", - "selector": "$[2:231584178474632390847141970017375815706539969331281128078915168015826259279872]", - "invalid_selector": true - }, { - "name": "union array slice, underflowing from value", - "selector": "$[-231584178474632390847141970017375815706539969331281128078915168015826259279872:1]", - "invalid_selector": true - }, { - "name": "union array slice, overflowing from value with negative step", - "selector": "$[231584178474632390847141970017375815706539969331281128078915168015826259279872:0:-1]", - "invalid_selector": true - }, { - "name": "union array slice, underflowing to value with negative step", - "selector": "$[3:-231584178474632390847141970017375815706539969331281128078915168015826259279872:-1]", - "invalid_selector": true - }, { - "name": "union array slice, overflowing step", - "selector": "$[1:10:231584178474632390847141970017375815706539969331281128078915168015826259279872]", - "invalid_selector": true - }, { - "name": "union array slice, underflowing step", - "selector": "$[-1:-10:-231584178474632390847141970017375815706539969331281128078915168015826259279872]", - "invalid_selector": true - } -]} diff --git a/tests/cts.rs b/tests/cts.rs index 6b174a4..5e235ee 100644 --- a/tests/cts.rs +++ b/tests/cts.rs @@ -39,7 +39,8 @@ mod tests { #[test] fn compliance_test_suite() { - let cts_json = fs::read_to_string("tests/cts.json").expect("failed to read cts.json"); + let cts_json = fs::read_to_string("jsonpath-compliance-test-suite/cts.json") + .expect("failed to read cts.json"); let suite: TestSuite = serde_json::from_str(&cts_json).expect("failed to deserialize cts.json"); @@ -89,10 +90,18 @@ mod tests { assert!(false, "find failed") // should not happen } } else { - if !t.invalid_selector { + if t.invalid_selector { + // print failure message + println!( + "{}: parsing `{}` failed with: {}", + t.name, + t.selector, + path.err().expect("should be an error") + ); + } else { assert!( path.is_ok(), - "{}: parsing {} should have succeeded but failed: {}", + "{}: parsing `{}` should have succeeded but failed: {}", t.name, t.selector, path.err().expect("should be an error")