]> git.lizzy.rs Git - rust.git/blob - src/tools/html-checker/main.rs
Rollup merge of #99880 - compiler-errors:escape-ascii-is-not-exact-size-iterator...
[rust.git] / src / tools / html-checker / main.rs
1 use rayon::iter::{ParallelBridge, ParallelIterator};
2 use std::env;
3 use std::path::Path;
4 use std::process::{Command, Output};
5
6 fn check_html_file(file: &Path) -> usize {
7     let to_mute = &[
8         // "disabled" on <link> or "autocomplete" on <select> emit this warning
9         "PROPRIETARY_ATTRIBUTE",
10         // It complains when multiple in the same page link to the same anchor for some reason...
11         "ANCHOR_NOT_UNIQUE",
12         // If a <span> contains only HTML elements and no text, it complains about it.
13         "TRIM_EMPTY_ELEMENT",
14         // FIXME: the three next warnings are about <pre> elements which are not supposed to
15         //        contain HTML. The solution here would be to replace them with a <div>
16         "MISSING_ENDTAG_BEFORE",
17         "INSERTING_TAG",
18         "DISCARDING_UNEXPECTED",
19         // This error is caused by nesting the Notable Traits tooltip within an <h4> tag.
20         // The solution is to avoid doing that, but we need to have the <h4> tags for accessibility
21         // reasons, and we need the Notable Traits tooltip to help everyone understand the Iterator
22         // combinators
23         "TAG_NOT_ALLOWED_IN",
24     ];
25     let to_mute_s = to_mute.join(",");
26     let mut command = Command::new("tidy");
27     command
28         .arg("-errors")
29         .arg("-quiet")
30         .arg("--mute-id") // this option is useful in case we want to mute more warnings
31         .arg("yes")
32         .arg("--mute")
33         .arg(&to_mute_s)
34         .arg(file);
35
36     let Output { status, stderr, .. } = command.output().expect("failed to run tidy command");
37     if status.success() {
38         0
39     } else {
40         let stderr = String::from_utf8(stderr).expect("String::from_utf8 failed...");
41         if stderr.is_empty() && status.code() != Some(2) {
42             0
43         } else {
44             eprintln!(
45                 "=> Errors for `{}` (error code: {}) <=",
46                 file.display(),
47                 status.code().unwrap_or(-1)
48             );
49             eprintln!("{}", stderr);
50             stderr.lines().count()
51         }
52     }
53 }
54
55 const DOCS_TO_CHECK: &[&str] =
56     &["alloc", "core", "proc_macro", "implementors", "src", "std", "test"];
57
58 // Returns the number of files read and the number of errors.
59 fn find_all_html_files(dir: &Path) -> (usize, usize) {
60     walkdir::WalkDir::new(dir)
61         .into_iter()
62         .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         .par_bridge()
70         .map(|entry| {
71             let entry = entry.expect("failed to read file");
72             if !entry.file_type().is_file() {
73                 return (0, 0);
74             }
75             let entry = entry.path();
76             // (Number of files processed, number of errors)
77             if entry.extension().and_then(|s| s.to_str()) == Some("html") {
78                 (1, check_html_file(&entry))
79             } else {
80                 (0, 0)
81             }
82         })
83         .reduce(|| (0, 0), |a, b| (a.0 + b.0, a.1 + b.1))
84 }
85
86 /// Default `tidy` command for macOS is too old that it does not have `mute-id` and `mute` options.
87 /// `tidy` on macOS Monterey was released on 31 October 2006, and the same date can be seen seven
88 /// years ago at <https://stackoverflow.com/questions/22283382/overwrite-osx-tidy>. Accordingly,
89 /// the macOS environment using pre-installed `tidy` should immediately suspend HTML checker process
90 /// and show a hint to install a newer one.
91 #[cfg(target_os = "macos")]
92 fn check_tidy_version() -> Result<(), String> {
93     let output = Command::new("tidy").arg("-v").output().expect("failed to run tidy command");
94     let version = String::from_utf8(output.stdout).expect("failed to read version of tidy command");
95     if version.contains("HTML Tidy for Mac OS X released on 31 October 2006") {
96         eprintln!("The pre-installed HTML Tidy for macOS is not supported.");
97         eprintln!("Consider installing a newer one and re-running.");
98         eprintln!("If you're using Homebrew, you can install it by the following command:");
99         eprintln!("    brew install tidy-html5");
100         eprintln!();
101         Err("HTML check failed: 1 error".to_string())
102     } else {
103         Ok(())
104     }
105 }
106
107 fn main() -> Result<(), String> {
108     let args = env::args().collect::<Vec<_>>();
109     if args.len() != 2 {
110         return Err(format!("Usage: {} <doc folder>", args[0]));
111     }
112     #[cfg(target_os = "macos")]
113     check_tidy_version()?;
114
115     println!("Running HTML checker...");
116
117     let (files_read, errors) = find_all_html_files(&Path::new(&args[1]));
118     println!("Done! Read {} files...", files_read);
119     if errors > 0 {
120         Err(format!("HTML check failed: {} errors", errors))
121     } else {
122         println!("No error found!");
123         Ok(())
124     }
125 }