]>
Commit | Line | Data |
---|---|---|
f25598a0 | 1 | use std::io::{BufWriter, Write}; |
f2b60f7d | 2 | |
f25598a0 FG |
3 | use anyhow::{bail, Result}; |
4 | use clap::Parser; | |
f2b60f7d FG |
5 | use fs_err as fs; |
6 | use rustdoc_json_types::{Crate, Id, FORMAT_VERSION}; | |
f25598a0 | 7 | use serde::Serialize; |
f2b60f7d FG |
8 | use serde_json::Value; |
9 | ||
10 | pub(crate) mod item_kind; | |
11 | mod json_find; | |
12 | mod validator; | |
13 | ||
f25598a0 | 14 | #[derive(Debug, PartialEq, Eq, Serialize, Clone)] |
f2b60f7d FG |
15 | struct Error { |
16 | kind: ErrorKind, | |
17 | id: Id, | |
18 | } | |
19 | ||
f25598a0 | 20 | #[derive(Debug, PartialEq, Eq, Serialize, Clone)] |
f2b60f7d | 21 | enum ErrorKind { |
f25598a0 | 22 | NotFound(Vec<json_find::Selector>), |
f2b60f7d FG |
23 | Custom(String), |
24 | } | |
25 | ||
f25598a0 FG |
26 | #[derive(Debug, Serialize)] |
27 | struct JsonOutput { | |
28 | path: String, | |
29 | errors: Vec<Error>, | |
30 | } | |
31 | ||
32 | #[derive(Parser)] | |
33 | struct Cli { | |
34 | /// The path to the json file to be linted | |
35 | path: String, | |
36 | ||
37 | /// Show verbose output | |
38 | #[arg(long)] | |
39 | verbose: bool, | |
40 | ||
41 | #[arg(long)] | |
42 | json_output: Option<String>, | |
43 | } | |
44 | ||
f2b60f7d | 45 | fn main() -> Result<()> { |
f25598a0 FG |
46 | let Cli { path, verbose, json_output } = Cli::parse(); |
47 | ||
f2b60f7d FG |
48 | let contents = fs::read_to_string(&path)?; |
49 | let krate: Crate = serde_json::from_str(&contents)?; | |
50 | assert_eq!(krate.format_version, FORMAT_VERSION); | |
51 | ||
f25598a0 FG |
52 | let krate_json: Value = serde_json::from_str(&contents)?; |
53 | ||
54 | let mut validator = validator::Validator::new(&krate, krate_json); | |
f2b60f7d FG |
55 | validator.check_crate(); |
56 | ||
f25598a0 FG |
57 | if let Some(json_output) = json_output { |
58 | let output = JsonOutput { path: path.clone(), errors: validator.errs.clone() }; | |
59 | let mut f = BufWriter::new(fs::File::create(json_output)?); | |
60 | serde_json::to_writer(&mut f, &output)?; | |
61 | f.flush()?; | |
62 | } | |
63 | ||
f2b60f7d FG |
64 | if !validator.errs.is_empty() { |
65 | for err in validator.errs { | |
66 | match err.kind { | |
f25598a0 FG |
67 | ErrorKind::NotFound(sels) => match &sels[..] { |
68 | [] => { | |
69 | unreachable!( | |
70 | "id {:?} must be in crate, or it wouldn't be reported as not found", | |
71 | err.id | |
72 | ) | |
73 | } | |
74 | [sel] => eprintln!( | |
75 | "{} not in index or paths, but refered to at '{}'", | |
76 | err.id.0, | |
77 | json_find::to_jsonpath(&sel) | |
78 | ), | |
79 | [sel, ..] => { | |
80 | if verbose { | |
81 | let sels = sels | |
82 | .iter() | |
83 | .map(json_find::to_jsonpath) | |
84 | .map(|i| format!("'{i}'")) | |
85 | .collect::<Vec<_>>() | |
86 | .join(", "); | |
87 | eprintln!( | |
88 | "{} not in index or paths, but refered to at {sels}", | |
89 | err.id.0 | |
90 | ); | |
91 | } else { | |
92 | eprintln!( | |
93 | "{} not in index or paths, but refered to at '{}' and {} more", | |
94 | err.id.0, | |
95 | json_find::to_jsonpath(&sel), | |
96 | sels.len() - 1, | |
97 | ) | |
98 | } | |
f2b60f7d | 99 | } |
f25598a0 | 100 | }, |
f2b60f7d FG |
101 | ErrorKind::Custom(msg) => eprintln!("{}: {}", err.id.0, msg), |
102 | } | |
103 | } | |
104 | bail!("Errors validating json {path}"); | |
105 | } | |
106 | ||
107 | Ok(()) | |
108 | } |