]> git.lizzy.rs Git - rust.git/blob - src/tools/html-checker/main.rs
Stabilize File::options()
[rust.git] / src / tools / html-checker / main.rs
1 use std::env;
2 use std::path::Path;
3 use std::process::{Command, Output};
4
5 fn check_html_file(file: &Path) -> usize {
6     let to_mute = &[
7         // "disabled" on <link> or "autocomplete" on <select> emit this warning
8         "PROPRIETARY_ATTRIBUTE",
9         // It complains when multiple in the same page link to the same anchor for some reason...
10         "ANCHOR_NOT_UNIQUE",
11         // If a <span> contains only HTML elements and no text, it complains about it.
12         "TRIM_EMPTY_ELEMENT",
13         // FIXME: the three next warnings are about <pre> elements which are not supposed to
14         //        contain HTML. The solution here would be to replace them with a <div>
15         "MISSING_ENDTAG_BEFORE",
16         "INSERTING_TAG",
17         "DISCARDING_UNEXPECTED",
18         // This error is caused by nesting the Notable Traits tooltip within an <h4> tag.
19         // The solution is to avoid doing that, but we need to have the <h4> tags for accessibility
20         // reasons, and we need the Notable Traits tooltip to help everyone understand the Iterator
21         // combinators
22         "TAG_NOT_ALLOWED_IN",
23     ];
24     let to_mute_s = to_mute.join(",");
25     let mut command = Command::new("tidy");
26     command
27         .arg("-errors")
28         .arg("-quiet")
29         .arg("--mute-id") // this option is useful in case we want to mute more warnings
30         .arg("yes")
31         .arg("--mute")
32         .arg(&to_mute_s)
33         .arg(file);
34
35     let Output { status, stderr, .. } = command.output().expect("failed to run tidy command");
36     if status.success() {
37         0
38     } else {
39         let stderr = String::from_utf8(stderr).expect("String::from_utf8 failed...");
40         if stderr.is_empty() && status.code() != Some(2) {
41             0
42         } else {
43             eprintln!(
44                 "=> Errors for `{}` (error code: {}) <=",
45                 file.display(),
46                 status.code().unwrap_or(-1)
47             );
48             eprintln!("{}", stderr);
49             stderr.lines().count()
50         }
51     }
52 }
53
54 const DOCS_TO_CHECK: &[&str] =
55     &["alloc", "core", "proc_macro", "implementors", "src", "std", "test"];
56
57 // Returns the number of files read and the number of errors.
58 fn find_all_html_files(dir: &Path) -> (usize, usize) {
59     let mut files_read = 0;
60     let mut errors = 0;
61
62     for entry in walkdir::WalkDir::new(dir).into_iter().filter_entry(|e| {
63         e.depth() != 1
64             || e.file_name()
65                 .to_str()
66                 .map(|s| DOCS_TO_CHECK.into_iter().any(|d| *d == s))
67                 .unwrap_or(false)
68     }) {
69         let entry = entry.expect("failed to read file");
70         if !entry.file_type().is_file() {
71             continue;
72         }
73         let entry = entry.path();
74         if entry.extension().and_then(|s| s.to_str()) == Some("html") {
75             errors += check_html_file(&entry);
76             files_read += 1;
77         }
78     }
79     (files_read, errors)
80 }
81
82 fn main() -> Result<(), String> {
83     let args = env::args().collect::<Vec<_>>();
84     if args.len() != 2 {
85         return Err(format!("Usage: {} <doc folder>", args[0]));
86     }
87
88     println!("Running HTML checker...");
89
90     let (files_read, errors) = find_all_html_files(&Path::new(&args[1]));
91     println!("Done! Read {} files...", files_read);
92     if errors > 0 {
93         Err(format!("HTML check failed: {} errors", errors))
94     } else {
95         println!("No error found!");
96         Ok(())
97     }
98 }