1
Fork 0

jsondocck: Find path to Id's not in index

This commit is contained in:
Nixon Enraght-Moony 2022-08-24 23:18:53 +01:00
parent 5f1bc6fc5e
commit 41d35a97f9
2 changed files with 99 additions and 3 deletions

View 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();
}
}
}
}
}

View file

@ -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(())