|
| 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 | + ]; |
| 19 | + let to_mute_s = to_mute.join(","); |
| 20 | + let mut command = Command::new("tidy"); |
| 21 | + command |
| 22 | + .arg("-errors") |
| 23 | + .arg("-quiet") |
| 24 | + .arg("--mute-id") // this option is useful in case we want to mute more warnings |
| 25 | + .arg("yes") |
| 26 | + .arg("--mute") |
| 27 | + .arg(&to_mute_s) |
| 28 | + .arg(file); |
| 29 | + |
| 30 | + let Output { status, stderr, .. } = command.output().expect("failed to run tidy command"); |
| 31 | + if status.success() { |
| 32 | + 0 |
| 33 | + } else { |
| 34 | + let stderr = String::from_utf8(stderr).expect("String::from_utf8 failed..."); |
| 35 | + if stderr.is_empty() && status.code() != Some(2) { |
| 36 | + 0 |
| 37 | + } else { |
| 38 | + eprintln!( |
| 39 | + "=> Errors for `{}` (error code: {}) <=", |
| 40 | + file.display(), |
| 41 | + status.code().unwrap_or(-1) |
| 42 | + ); |
| 43 | + eprintln!("{}", stderr); |
| 44 | + stderr.lines().count() |
| 45 | + } |
| 46 | + } |
| 47 | +} |
| 48 | + |
| 49 | +const DOCS_TO_CHECK: &[&str] = |
| 50 | + &["alloc", "core", "proc_macro", "implementors", "src", "std", "test"]; |
| 51 | + |
| 52 | +// Returns the number of files read and the number of errors. |
| 53 | +fn find_all_html_files(dir: &Path) -> (usize, usize) { |
| 54 | + let mut files_read = 0; |
| 55 | + let mut errors = 0; |
| 56 | + |
| 57 | + for entry in walkdir::WalkDir::new(dir).into_iter().filter_entry(|e| { |
| 58 | + e.depth() != 1 |
| 59 | + || e.file_name() |
| 60 | + .to_str() |
| 61 | + .map(|s| DOCS_TO_CHECK.into_iter().any(|d| *d == s)) |
| 62 | + .unwrap_or(false) |
| 63 | + }) { |
| 64 | + let entry = entry.expect("failed to read file"); |
| 65 | + if !entry.file_type().is_file() { |
| 66 | + continue; |
| 67 | + } |
| 68 | + let entry = entry.path(); |
| 69 | + if entry.extension().and_then(|s| s.to_str()) == Some("html") { |
| 70 | + errors += check_html_file(&entry); |
| 71 | + files_read += 1; |
| 72 | + } |
| 73 | + } |
| 74 | + (files_read, errors) |
| 75 | +} |
| 76 | + |
| 77 | +fn main() -> Result<(), String> { |
| 78 | + let args = env::args().collect::<Vec<_>>(); |
| 79 | + if args.len() != 2 { |
| 80 | + return Err(format!("Usage: {} <doc folder>", args[0])); |
| 81 | + } |
| 82 | + |
| 83 | + println!("Running HTML checker..."); |
| 84 | + |
| 85 | + let (files_read, errors) = find_all_html_files(&Path::new(&args[1])); |
| 86 | + println!("Done! Read {} files...", files_read); |
| 87 | + if errors > 0 { |
| 88 | + Err(format!("HTML check failed: {} errors", errors)) |
| 89 | + } else { |
| 90 | + println!("No error found!"); |
| 91 | + Ok(()) |
| 92 | + } |
| 93 | +} |
0 commit comments