jsondocck: Find path to Id's not in index
This commit is contained in:
parent
5f1bc6fc5e
commit
41d35a97f9
2 changed files with 99 additions and 3 deletions
74
src/tools/jsondoclint/src/json_find.rs
Normal file
74
src/tools/jsondoclint/src/json_find.rs
Normal file
|
@ -0,0 +1,74 @@
|
|||
use std::fmt::Write;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SelectorPart {
|
||||
Field(String),
|
||||
Index(usize),
|
||||
}
|
||||
|
||||
pub type Selector = Vec<SelectorPart>;
|
||||
|
||||
pub fn to_jsonpath(sel: &Selector) -> String {
|
||||
let mut s = String::from("$");
|
||||
for part in sel {
|
||||
match part {
|
||||
SelectorPart::Field(name) => {
|
||||
if is_jsonpath_safe(name) {
|
||||
write!(&mut s, ".{}", name).unwrap();
|
||||
} else {
|
||||
// This is probably wrong in edge cases, but all Id's are
|
||||
// just ascii alphanumerics, `-` `_`, and `:`
|
||||
write!(&mut s, "[{name:?}]").unwrap();
|
||||
}
|
||||
}
|
||||
SelectorPart::Index(idx) => write!(&mut s, "[{idx}]").unwrap(),
|
||||
}
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
fn is_jsonpath_safe(s: &str) -> bool {
|
||||
s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')
|
||||
}
|
||||
|
||||
pub fn find_selector(haystack: &Value, needle: &Value) -> Vec<Selector> {
|
||||
let mut result = Vec::new();
|
||||
let mut sel = Selector::new();
|
||||
find_selector_recursive(haystack, needle, &mut result, &mut sel);
|
||||
result
|
||||
}
|
||||
|
||||
fn find_selector_recursive(
|
||||
haystack: &Value,
|
||||
needle: &Value,
|
||||
result: &mut Vec<Selector>,
|
||||
pos: &mut Selector,
|
||||
) {
|
||||
if needle == haystack {
|
||||
result.push(pos.clone());
|
||||
// Haystack cant both contain needle and be needle
|
||||
} else {
|
||||
match haystack {
|
||||
Value::Null => {}
|
||||
Value::Bool(_) => {}
|
||||
Value::Number(_) => {}
|
||||
Value::String(_) => {}
|
||||
Value::Array(arr) => {
|
||||
for (idx, subhaystack) in arr.iter().enumerate() {
|
||||
pos.push(SelectorPart::Index(idx));
|
||||
find_selector_recursive(subhaystack, needle, result, pos);
|
||||
pos.pop().unwrap();
|
||||
}
|
||||
}
|
||||
Value::Object(obj) => {
|
||||
for (key, subhaystack) in obj {
|
||||
pos.push(SelectorPart::Field(key.clone()));
|
||||
find_selector_recursive(subhaystack, needle, result, pos);
|
||||
pos.pop().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,8 +3,10 @@ use std::env;
|
|||
use anyhow::{anyhow, bail, Result};
|
||||
use fs_err as fs;
|
||||
use rustdoc_json_types::{Crate, Id, FORMAT_VERSION};
|
||||
use serde_json::Value;
|
||||
|
||||
pub(crate) mod item_kind;
|
||||
mod json_find;
|
||||
mod validator;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -21,8 +23,10 @@ enum ErrorKind {
|
|||
|
||||
fn main() -> Result<()> {
|
||||
let path = env::args().nth(1).ok_or_else(|| anyhow!("no path given"))?;
|
||||
let contents = fs::read_to_string(path)?;
|
||||
let contents = fs::read_to_string(&path)?;
|
||||
let krate: Crate = serde_json::from_str(&contents)?;
|
||||
// TODO: Only load if nessessary.
|
||||
let krate_json: Value = serde_json::from_str(&contents)?;
|
||||
assert_eq!(krate.format_version, FORMAT_VERSION);
|
||||
|
||||
let mut validator = validator::Validator::new(&krate);
|
||||
|
@ -31,11 +35,29 @@ fn main() -> Result<()> {
|
|||
if !validator.errs.is_empty() {
|
||||
for err in validator.errs {
|
||||
match err.kind {
|
||||
ErrorKind::NotFound => eprintln!("{}: Not Found", err.id.0),
|
||||
ErrorKind::NotFound => {
|
||||
let sels =
|
||||
json_find::find_selector(&krate_json, &Value::String(err.id.0.clone()));
|
||||
match &sels[..] {
|
||||
[] => unreachable!(
|
||||
"id must be in crate, or it wouldn't be reported as not found"
|
||||
),
|
||||
[sel] => eprintln!(
|
||||
"{} not in index or paths, but refered to at '{}'",
|
||||
err.id.0,
|
||||
json_find::to_jsonpath(&sel)
|
||||
),
|
||||
[sel, ..] => eprintln!(
|
||||
"{} not in index or paths, but refered to at '{}' and more",
|
||||
err.id.0,
|
||||
json_find::to_jsonpath(&sel)
|
||||
),
|
||||
}
|
||||
}
|
||||
ErrorKind::Custom(msg) => eprintln!("{}: {}", err.id.0, msg),
|
||||
}
|
||||
}
|
||||
bail!("Errors validating json");
|
||||
bail!("Errors validating json {path}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue