diff options
Diffstat (limited to 'src/tools/jsondoclint/src/main.rs')
-rw-r--r-- | src/tools/jsondoclint/src/main.rs | 98 |
1 files changed, 71 insertions, 27 deletions
diff --git a/src/tools/jsondoclint/src/main.rs b/src/tools/jsondoclint/src/main.rs index fc54c421b..05e938f4f 100644 --- a/src/tools/jsondoclint/src/main.rs +++ b/src/tools/jsondoclint/src/main.rs @@ -1,59 +1,103 @@ -use std::env; +use std::io::{BufWriter, Write}; -use anyhow::{anyhow, bail, Result}; +use anyhow::{bail, Result}; +use clap::Parser; use fs_err as fs; use rustdoc_json_types::{Crate, Id, FORMAT_VERSION}; +use serde::Serialize; use serde_json::Value; pub(crate) mod item_kind; mod json_find; mod validator; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Clone)] struct Error { kind: ErrorKind, id: Id, } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Clone)] enum ErrorKind { - NotFound, + NotFound(Vec<json_find::Selector>), Custom(String), } +#[derive(Debug, Serialize)] +struct JsonOutput { + path: String, + errors: Vec<Error>, +} + +#[derive(Parser)] +struct Cli { + /// The path to the json file to be linted + path: String, + + /// Show verbose output + #[arg(long)] + verbose: bool, + + #[arg(long)] + json_output: Option<String>, +} + fn main() -> Result<()> { - let path = env::args().nth(1).ok_or_else(|| anyhow!("no path given"))?; + let Cli { path, verbose, json_output } = Cli::parse(); + let contents = fs::read_to_string(&path)?; let krate: Crate = serde_json::from_str(&contents)?; assert_eq!(krate.format_version, FORMAT_VERSION); - let mut validator = validator::Validator::new(&krate); + let krate_json: Value = serde_json::from_str(&contents)?; + + let mut validator = validator::Validator::new(&krate, krate_json); validator.check_crate(); + if let Some(json_output) = json_output { + let output = JsonOutput { path: path.clone(), errors: validator.errs.clone() }; + let mut f = BufWriter::new(fs::File::create(json_output)?); + serde_json::to_writer(&mut f, &output)?; + f.flush()?; + } + if !validator.errs.is_empty() { for err in validator.errs { match err.kind { - ErrorKind::NotFound => { - let krate_json: Value = serde_json::from_str(&contents)?; - - 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::NotFound(sels) => match &sels[..] { + [] => { + unreachable!( + "id {:?} must be in crate, or it wouldn't be reported as not found", + err.id + ) + } + [sel] => eprintln!( + "{} not in index or paths, but refered to at '{}'", + err.id.0, + json_find::to_jsonpath(&sel) + ), + [sel, ..] => { + if verbose { + let sels = sels + .iter() + .map(json_find::to_jsonpath) + .map(|i| format!("'{i}'")) + .collect::<Vec<_>>() + .join(", "); + eprintln!( + "{} not in index or paths, but refered to at {sels}", + err.id.0 + ); + } else { + eprintln!( + "{} not in index or paths, but refered to at '{}' and {} more", + err.id.0, + json_find::to_jsonpath(&sel), + sels.len() - 1, + ) + } } - } + }, ErrorKind::Custom(msg) => eprintln!("{}: {}", err.id.0, msg), } } |