From a00eb7ee1d8f5b38b94410940e87af9c10ed458a Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Sat, 20 Feb 2021 00:43:02 +0000 Subject: [PATCH 1/6] Add @is command to jsondocck --- src/test/rustdoc-json/nested.rs | 20 ++++++++++---------- src/tools/jsondocck/src/main.rs | 18 +++++++++++++++++- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/test/rustdoc-json/nested.rs b/src/test/rustdoc-json/nested.rs index 7e705255d983d..f32692b70cd2b 100644 --- a/src/test/rustdoc-json/nested.rs +++ b/src/test/rustdoc-json/nested.rs @@ -1,24 +1,24 @@ // edition:2018 -// @has nested.json "$.index[*][?(@.name=='nested')].kind" \"module\" -// @has - "$.index[*][?(@.name=='nested')].inner.is_crate" true +// @is nested.json "$.index[*][?(@.name=='nested')].kind" \"module\" +// @is - "$.index[*][?(@.name=='nested')].inner.is_crate" true // @count - "$.index[*][?(@.name=='nested')].inner.items[*]" 1 -// @has nested.json "$.index[*][?(@.name=='l1')].kind" \"module\" -// @has - "$.index[*][?(@.name=='l1')].inner.is_crate" false +// @is nested.json "$.index[*][?(@.name=='l1')].kind" \"module\" +// @is - "$.index[*][?(@.name=='l1')].inner.is_crate" false // @count - "$.index[*][?(@.name=='l1')].inner.items[*]" 2 pub mod l1 { - // @has nested.json "$.index[*][?(@.name=='l3')].kind" \"module\" - // @has - "$.index[*][?(@.name=='l3')].inner.is_crate" false + // @is nested.json "$.index[*][?(@.name=='l3')].kind" \"module\" + // @is - "$.index[*][?(@.name=='l3')].inner.is_crate" false // @count - "$.index[*][?(@.name=='l3')].inner.items[*]" 1 pub mod l3 { - // @has nested.json "$.index[*][?(@.name=='L4')].kind" \"struct\" - // @has - "$.index[*][?(@.name=='L4')].inner.struct_type" \"unit\" + // @is nested.json "$.index[*][?(@.name=='L4')].kind" \"struct\" + // @is - "$.index[*][?(@.name=='L4')].inner.struct_type" \"unit\" pub struct L4; } - // @has nested.json "$.index[*][?(@.inner.span=='l3::L4')].kind" \"import\" - // @has - "$.index[*][?(@.inner.span=='l3::L4')].inner.glob" false + // @is nested.json "$.index[*][?(@.inner.span=='l3::L4')].kind" \"import\" + // @is - "$.index[*][?(@.inner.span=='l3::L4')].inner.glob" false pub use l3::L4; } diff --git a/src/tools/jsondocck/src/main.rs b/src/tools/jsondocck/src/main.rs index 6ec292aba6495..2afb66b969685 100644 --- a/src/tools/jsondocck/src/main.rs +++ b/src/tools/jsondocck/src/main.rs @@ -48,13 +48,14 @@ pub struct Command { pub enum CommandKind { Has, Count, + Is, } impl CommandKind { fn validate(&self, args: &[String], command_num: usize, lineno: usize) -> bool { let count = match self { CommandKind::Has => (1..=3).contains(&args.len()), - CommandKind::Count => 3 == args.len(), + CommandKind::Count | CommandKind::Is => 3 == args.len(), }; if !count { @@ -83,6 +84,7 @@ impl fmt::Display for CommandKind { let text = match self { CommandKind::Has => "has", CommandKind::Count => "count", + CommandKind::Is => "is", }; write!(f, "{}", text) } @@ -127,6 +129,7 @@ fn get_commands(template: &str) -> Result, ()> { let cmd = match cmd { "has" => CommandKind::Has, "count" => CommandKind::Count, + "is" => CommandKind::Is, _ => { print_err(&format!("Unrecognized command name `@{}`", cmd), lineno); errors = true; @@ -180,6 +183,7 @@ fn get_commands(template: &str) -> Result, ()> { /// Performs the actual work of ensuring a command passes. Generally assumes the command /// is syntactically valid. fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { + // FIXME: Be more granular about why, (eg syntax error, count not equal) let result = match command.kind { CommandKind::Has => { match command.args.len() { @@ -220,6 +224,18 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { Err(_) => false, } } + CommandKind::Is => { + // @has = check *exactly one* item matched by path, and it equals value + assert_eq!(command.args.len(), 3); + let val = cache.get_value(&command.args[0])?; + match select(&val, &command.args[1]) { + Ok(results) => { + let pat: Value = serde_json::from_str(&command.args[2]).unwrap(); + results.len() == 1 && *results[0] == pat + } + Err(_) => false, + } + } }; if result == command.negated { From cd5f603c314edcf2d75656ac86fc1d303aacfb83 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Sat, 20 Feb 2021 01:25:09 +0000 Subject: [PATCH 2/6] Implement @set --- src/test/rustdoc-json/nested.rs | 3 +++ src/tools/jsondocck/src/cache.rs | 2 ++ src/tools/jsondocck/src/main.rs | 21 +++++++++++++++++++++ 3 files changed, 26 insertions(+) diff --git a/src/test/rustdoc-json/nested.rs b/src/test/rustdoc-json/nested.rs index f32692b70cd2b..2e0179113acf4 100644 --- a/src/test/rustdoc-json/nested.rs +++ b/src/test/rustdoc-json/nested.rs @@ -7,15 +7,18 @@ // @is nested.json "$.index[*][?(@.name=='l1')].kind" \"module\" // @is - "$.index[*][?(@.name=='l1')].inner.is_crate" false // @count - "$.index[*][?(@.name=='l1')].inner.items[*]" 2 +// @set l1_id = - "$.index[*][?(@.name=='l1')].id" pub mod l1 { // @is nested.json "$.index[*][?(@.name=='l3')].kind" \"module\" // @is - "$.index[*][?(@.name=='l3')].inner.is_crate" false // @count - "$.index[*][?(@.name=='l3')].inner.items[*]" 1 + // @set l3_id = - "$.index[*][?(@.name=='l3')].id" pub mod l3 { // @is nested.json "$.index[*][?(@.name=='L4')].kind" \"struct\" // @is - "$.index[*][?(@.name=='L4')].inner.struct_type" \"unit\" + // @set l4_id = - "$.index[*][?(@.name=='L4')].id" pub struct L4; } // @is nested.json "$.index[*][?(@.inner.span=='l3::L4')].kind" \"import\" diff --git a/src/tools/jsondocck/src/cache.rs b/src/tools/jsondocck/src/cache.rs index b742f0eb3ee55..8a6a911321c34 100644 --- a/src/tools/jsondocck/src/cache.rs +++ b/src/tools/jsondocck/src/cache.rs @@ -9,6 +9,7 @@ pub struct Cache { root: PathBuf, files: HashMap, values: HashMap, + pub variables: HashMap, last_path: Option, } @@ -19,6 +20,7 @@ impl Cache { root: Path::new(doc_dir).to_owned(), files: HashMap::new(), values: HashMap::new(), + variables: HashMap::new(), last_path: None, } } diff --git a/src/tools/jsondocck/src/main.rs b/src/tools/jsondocck/src/main.rs index 2afb66b969685..9f231842c60ea 100644 --- a/src/tools/jsondocck/src/main.rs +++ b/src/tools/jsondocck/src/main.rs @@ -49,6 +49,7 @@ pub enum CommandKind { Has, Count, Is, + Set, } impl CommandKind { @@ -56,6 +57,7 @@ impl CommandKind { let count = match self { CommandKind::Has => (1..=3).contains(&args.len()), CommandKind::Count | CommandKind::Is => 3 == args.len(), + CommandKind::Set => 4 == args.len(), }; if !count { @@ -85,6 +87,7 @@ impl fmt::Display for CommandKind { CommandKind::Has => "has", CommandKind::Count => "count", CommandKind::Is => "is", + CommandKind::Set => "set", }; write!(f, "{}", text) } @@ -130,6 +133,7 @@ fn get_commands(template: &str) -> Result, ()> { "has" => CommandKind::Has, "count" => CommandKind::Count, "is" => CommandKind::Is, + "set" => CommandKind::Set, _ => { print_err(&format!("Unrecognized command name `@{}`", cmd), lineno); errors = true; @@ -236,6 +240,23 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { Err(_) => false, } } + // FIXME, Figure out semantics for @!set + CommandKind::Set => { + // @set = + assert_eq!(command.args.len(), 4); + assert_eq!(command.args[1], "=", "Expected an `=`"); + let val = cache.get_value(&command.args[2])?; + + match select(&val, &command.args[3]) { + Ok(results) => { + assert_eq!(results.len(), 1); + let r = cache.variables.insert(command.args[0].clone(), results[0].clone()); + assert!(r.is_none(), "Name collision"); + true + } + Err(_) => false, + } + } }; if result == command.negated { From dd4b938c7f0c7704582a786f973bcfb17e523e94 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Sat, 20 Feb 2021 01:47:17 +0000 Subject: [PATCH 3/6] Implement using @set values --- src/test/rustdoc-json/nested.rs | 3 ++- src/tools/jsondocck/src/main.rs | 22 +++++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/test/rustdoc-json/nested.rs b/src/test/rustdoc-json/nested.rs index 2e0179113acf4..a3d4935f49614 100644 --- a/src/test/rustdoc-json/nested.rs +++ b/src/test/rustdoc-json/nested.rs @@ -7,18 +7,19 @@ // @is nested.json "$.index[*][?(@.name=='l1')].kind" \"module\" // @is - "$.index[*][?(@.name=='l1')].inner.is_crate" false // @count - "$.index[*][?(@.name=='l1')].inner.items[*]" 2 -// @set l1_id = - "$.index[*][?(@.name=='l1')].id" pub mod l1 { // @is nested.json "$.index[*][?(@.name=='l3')].kind" \"module\" // @is - "$.index[*][?(@.name=='l3')].inner.is_crate" false // @count - "$.index[*][?(@.name=='l3')].inner.items[*]" 1 // @set l3_id = - "$.index[*][?(@.name=='l3')].id" + // @has - "$.index[*][?(@.name=='l1')].inner.items[*]" $l3_id pub mod l3 { // @is nested.json "$.index[*][?(@.name=='L4')].kind" \"struct\" // @is - "$.index[*][?(@.name=='L4')].inner.struct_type" \"unit\" // @set l4_id = - "$.index[*][?(@.name=='L4')].id" + // @has - "$.index[*][?(@.name=='l3')].inner.items[*]" $l4_id pub struct L4; } // @is nested.json "$.index[*][?(@.inner.span=='l3::L4')].kind" \"import\" diff --git a/src/tools/jsondocck/src/main.rs b/src/tools/jsondocck/src/main.rs index 9f231842c60ea..8cb9564531a98 100644 --- a/src/tools/jsondocck/src/main.rs +++ b/src/tools/jsondocck/src/main.rs @@ -207,9 +207,15 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { let val = cache.get_value(&command.args[0])?; match select(&val, &command.args[1]) { Ok(results) => { - let pat: Value = serde_json::from_str(&command.args[2]).unwrap(); - - !results.is_empty() && results.into_iter().any(|val| *val == pat) + // FIXME: Share the pat getting code with the `Is` branch. + let v_holder; + let pat: &Value = if command.args[2].starts_with("$") { + &cache.variables[&command.args[2][1..]] + } else { + v_holder = serde_json::from_str(&command.args[2]).unwrap(); + &v_holder + }; + !results.is_empty() && results.into_iter().any(|val| val == pat) } Err(_) => false, } @@ -234,8 +240,14 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { let val = cache.get_value(&command.args[0])?; match select(&val, &command.args[1]) { Ok(results) => { - let pat: Value = serde_json::from_str(&command.args[2]).unwrap(); - results.len() == 1 && *results[0] == pat + let v_holder; + let pat: &Value = if command.args[2].starts_with("$") { + &cache.variables[&command.args[2][1..]] + } else { + v_holder = serde_json::from_str(&command.args[2]).unwrap(); + &v_holder + }; + results.len() == 1 && results[0] == pat } Err(_) => false, } From a22d948eb0cd509b7ea52c3c615253d03ca9914b Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Sun, 21 Feb 2021 19:45:32 +0000 Subject: [PATCH 4/6] Apply suggestions from code review Co-authored-by: Joshua Nelson --- src/tools/jsondocck/src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/jsondocck/src/main.rs b/src/tools/jsondocck/src/main.rs index 8cb9564531a98..9c9d49b821f4a 100644 --- a/src/tools/jsondocck/src/main.rs +++ b/src/tools/jsondocck/src/main.rs @@ -187,7 +187,7 @@ fn get_commands(template: &str) -> Result, ()> { /// Performs the actual work of ensuring a command passes. Generally assumes the command /// is syntactically valid. fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { - // FIXME: Be more granular about why, (eg syntax error, count not equal) + // FIXME: Be more granular about why, (e.g. syntax error, count not equal) let result = match command.kind { CommandKind::Has => { match command.args.len() { @@ -215,7 +215,7 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { v_holder = serde_json::from_str(&command.args[2]).unwrap(); &v_holder }; - !results.is_empty() && results.into_iter().any(|val| val == pat) + results.contains(pat) } Err(_) => false, } @@ -263,7 +263,7 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { Ok(results) => { assert_eq!(results.len(), 1); let r = cache.variables.insert(command.args[0].clone(), results[0].clone()); - assert!(r.is_none(), "Name collision"); + assert!(r.is_none(), "Name collision: {} is duplicated", command.args[0]); true } Err(_) => false, From ba22a69d964283d388a362830e73a992f024a6bf Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Sun, 21 Feb 2021 20:10:57 +0000 Subject: [PATCH 5/6] Extract string_to_value to its own function --- src/tools/jsondocck/src/main.rs | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/tools/jsondocck/src/main.rs b/src/tools/jsondocck/src/main.rs index 9c9d49b821f4a..e3334e559db65 100644 --- a/src/tools/jsondocck/src/main.rs +++ b/src/tools/jsondocck/src/main.rs @@ -2,6 +2,7 @@ use jsonpath_lib::select; use lazy_static::lazy_static; use regex::{Regex, RegexBuilder}; use serde_json::Value; +use std::borrow::Cow; use std::{env, fmt, fs}; mod cache; @@ -207,15 +208,8 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { let val = cache.get_value(&command.args[0])?; match select(&val, &command.args[1]) { Ok(results) => { - // FIXME: Share the pat getting code with the `Is` branch. - let v_holder; - let pat: &Value = if command.args[2].starts_with("$") { - &cache.variables[&command.args[2][1..]] - } else { - v_holder = serde_json::from_str(&command.args[2]).unwrap(); - &v_holder - }; - results.contains(pat) + let pat = string_to_value(&command.args[2], cache); + results.contains(&pat.as_ref()) } Err(_) => false, } @@ -240,14 +234,8 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { let val = cache.get_value(&command.args[0])?; match select(&val, &command.args[1]) { Ok(results) => { - let v_holder; - let pat: &Value = if command.args[2].starts_with("$") { - &cache.variables[&command.args[2][1..]] - } else { - v_holder = serde_json::from_str(&command.args[2]).unwrap(); - &v_holder - }; - results.len() == 1 && results[0] == pat + let pat = string_to_value(&command.args[2], cache); + results.len() == 1 && results[0] == pat.as_ref() } Err(_) => false, } @@ -296,3 +284,11 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { Ok(()) } } + +fn string_to_value<'a>(s: &str, cache: &'a Cache) -> Cow<'a, Value> { + if s.starts_with("$") { + Cow::Borrowed(&cache.variables[&s[1..]]) + } else { + Cow::Owned(serde_json::from_str(s).unwrap()) + } +} From 4c949a455df81924b37dc41f2edf1071ea329fb5 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Mon, 22 Feb 2021 10:33:33 +0000 Subject: [PATCH 6/6] Simplify Error Handling. --- src/tools/jsondocck/src/main.rs | 50 ++++++++++++++------------------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/src/tools/jsondocck/src/main.rs b/src/tools/jsondocck/src/main.rs index e3334e559db65..5020a4917a00a 100644 --- a/src/tools/jsondocck/src/main.rs +++ b/src/tools/jsondocck/src/main.rs @@ -197,22 +197,15 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { // @has = check path exists 2 => { let val = cache.get_value(&command.args[0])?; - - match select(&val, &command.args[1]) { - Ok(results) => !results.is_empty(), - Err(_) => false, - } + let results = select(&val, &command.args[1]).unwrap(); + !results.is_empty() } // @has = check *any* item matched by path equals value 3 => { let val = cache.get_value(&command.args[0])?; - match select(&val, &command.args[1]) { - Ok(results) => { - let pat = string_to_value(&command.args[2], cache); - results.contains(&pat.as_ref()) - } - Err(_) => false, - } + let results = select(&val, &command.args[1]).unwrap(); + let pat = string_to_value(&command.args[2], cache); + results.contains(&pat.as_ref()) } _ => unreachable!(), } @@ -223,38 +216,37 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { let expected: usize = command.args[2].parse().unwrap(); let val = cache.get_value(&command.args[0])?; - match select(&val, &command.args[1]) { - Ok(results) => results.len() == expected, - Err(_) => false, - } + let results = select(&val, &command.args[1]).unwrap(); + results.len() == expected } CommandKind::Is => { // @has = check *exactly one* item matched by path, and it equals value assert_eq!(command.args.len(), 3); let val = cache.get_value(&command.args[0])?; - match select(&val, &command.args[1]) { - Ok(results) => { - let pat = string_to_value(&command.args[2], cache); - results.len() == 1 && results[0] == pat.as_ref() - } - Err(_) => false, - } + let results = select(&val, &command.args[1]).unwrap(); + let pat = string_to_value(&command.args[2], cache); + results.len() == 1 && results[0] == pat.as_ref() } - // FIXME, Figure out semantics for @!set CommandKind::Set => { // @set = assert_eq!(command.args.len(), 4); assert_eq!(command.args[1], "=", "Expected an `=`"); let val = cache.get_value(&command.args[2])?; - - match select(&val, &command.args[3]) { - Ok(results) => { - assert_eq!(results.len(), 1); + let results = select(&val, &command.args[3]).unwrap(); + assert_eq!(results.len(), 1); + match results.len() { + 0 => false, + 1 => { let r = cache.variables.insert(command.args[0].clone(), results[0].clone()); assert!(r.is_none(), "Name collision: {} is duplicated", command.args[0]); true } - Err(_) => false, + _ => { + panic!( + "Got multiple results in `@set` for `{}`: {:?}", + &command.args[3], results + ); + } } } };