Skip to content

Commit 41d35a9

Browse files
committed
jsondocck: Find path to Id's not in index
1 parent 5f1bc6f commit 41d35a9

File tree

2 files changed

+99
-3
lines changed

2 files changed

+99
-3
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
use std::fmt::Write;
2+
3+
use serde_json::Value;
4+
5+
#[derive(Debug, Clone)]
6+
pub enum SelectorPart {
7+
Field(String),
8+
Index(usize),
9+
}
10+
11+
pub type Selector = Vec<SelectorPart>;
12+
13+
pub fn to_jsonpath(sel: &Selector) -> String {
14+
let mut s = String::from("$");
15+
for part in sel {
16+
match part {
17+
SelectorPart::Field(name) => {
18+
if is_jsonpath_safe(name) {
19+
write!(&mut s, ".{}", name).unwrap();
20+
} else {
21+
// This is probably wrong in edge cases, but all Id's are
22+
// just ascii alphanumerics, `-` `_`, and `:`
23+
write!(&mut s, "[{name:?}]").unwrap();
24+
}
25+
}
26+
SelectorPart::Index(idx) => write!(&mut s, "[{idx}]").unwrap(),
27+
}
28+
}
29+
s
30+
}
31+
32+
fn is_jsonpath_safe(s: &str) -> bool {
33+
s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')
34+
}
35+
36+
pub fn find_selector(haystack: &Value, needle: &Value) -> Vec<Selector> {
37+
let mut result = Vec::new();
38+
let mut sel = Selector::new();
39+
find_selector_recursive(haystack, needle, &mut result, &mut sel);
40+
result
41+
}
42+
43+
fn find_selector_recursive(
44+
haystack: &Value,
45+
needle: &Value,
46+
result: &mut Vec<Selector>,
47+
pos: &mut Selector,
48+
) {
49+
if needle == haystack {
50+
result.push(pos.clone());
51+
// Haystack cant both contain needle and be needle
52+
} else {
53+
match haystack {
54+
Value::Null => {}
55+
Value::Bool(_) => {}
56+
Value::Number(_) => {}
57+
Value::String(_) => {}
58+
Value::Array(arr) => {
59+
for (idx, subhaystack) in arr.iter().enumerate() {
60+
pos.push(SelectorPart::Index(idx));
61+
find_selector_recursive(subhaystack, needle, result, pos);
62+
pos.pop().unwrap();
63+
}
64+
}
65+
Value::Object(obj) => {
66+
for (key, subhaystack) in obj {
67+
pos.push(SelectorPart::Field(key.clone()));
68+
find_selector_recursive(subhaystack, needle, result, pos);
69+
pos.pop().unwrap();
70+
}
71+
}
72+
}
73+
}
74+
}

src/tools/jsondoclint/src/main.rs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ use std::env;
33
use anyhow::{anyhow, bail, Result};
44
use fs_err as fs;
55
use rustdoc_json_types::{Crate, Id, FORMAT_VERSION};
6+
use serde_json::Value;
67

78
pub(crate) mod item_kind;
9+
mod json_find;
810
mod validator;
911

1012
#[derive(Debug)]
@@ -21,8 +23,10 @@ enum ErrorKind {
2123

2224
fn main() -> Result<()> {
2325
let path = env::args().nth(1).ok_or_else(|| anyhow!("no path given"))?;
24-
let contents = fs::read_to_string(path)?;
26+
let contents = fs::read_to_string(&path)?;
2527
let krate: Crate = serde_json::from_str(&contents)?;
28+
// TODO: Only load if nessessary.
29+
let krate_json: Value = serde_json::from_str(&contents)?;
2630
assert_eq!(krate.format_version, FORMAT_VERSION);
2731

2832
let mut validator = validator::Validator::new(&krate);
@@ -31,11 +35,29 @@ fn main() -> Result<()> {
3135
if !validator.errs.is_empty() {
3236
for err in validator.errs {
3337
match err.kind {
34-
ErrorKind::NotFound => eprintln!("{}: Not Found", err.id.0),
38+
ErrorKind::NotFound => {
39+
let sels =
40+
json_find::find_selector(&krate_json, &Value::String(err.id.0.clone()));
41+
match &sels[..] {
42+
[] => unreachable!(
43+
"id must be in crate, or it wouldn't be reported as not found"
44+
),
45+
[sel] => eprintln!(
46+
"{} not in index or paths, but refered to at '{}'",
47+
err.id.0,
48+
json_find::to_jsonpath(&sel)
49+
),
50+
[sel, ..] => eprintln!(
51+
"{} not in index or paths, but refered to at '{}' and more",
52+
err.id.0,
53+
json_find::to_jsonpath(&sel)
54+
),
55+
}
56+
}
3557
ErrorKind::Custom(msg) => eprintln!("{}: {}", err.id.0, msg),
3658
}
3759
}
38-
bail!("Errors validating json");
60+
bail!("Errors validating json {path}");
3961
}
4062

4163
Ok(())

0 commit comments

Comments
 (0)