tidy/rustdoc_templates.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
//! Tidy check to ensure that rustdoc templates didn't forget a `{# #}` to strip extra whitespace
//! characters.
use std::ffi::OsStr;
use std::path::Path;
use ignore::DirEntry;
use crate::walk::walk;
// Array containing `("beginning of tag", "end of tag")`.
const TAGS: &[(&str, &str)] = &[("{#", "#}"), ("{%", "%}"), ("{{", "}}")];
pub fn check(librustdoc_path: &Path, bad: &mut bool) {
walk(
&librustdoc_path.join("html/templates"),
|path, is_dir| is_dir || !path.extension().is_some_and(|ext| ext == OsStr::new("html")),
&mut |path: &DirEntry, file_content: &str| {
let mut lines = file_content.lines().enumerate().peekable();
while let Some((pos, line)) = lines.next() {
let line = line.trim();
if let Some(need_next_line_check) = TAGS.iter().find_map(|(tag, end_tag)| {
// We first check if the line ends with a jinja tag.
if !line.ends_with(end_tag) {
return None;
// Then we check if this a comment tag.
} else if *tag != "{#" {
return Some(false);
// And finally we check if the comment is empty (ie, only there to strip
// extra whitespace characters).
} else if let Some(start_pos) = line.rfind(tag) {
Some(line[start_pos + 2..].trim() == "#}")
} else {
Some(false)
}
}) {
// All good, the line is ending is a jinja tag. But maybe this tag is useless
// if the next line starts with a jinja tag as well!
//
// However, only (empty) comment jinja tags are concerned about it.
if need_next_line_check
&& lines.peek().is_some_and(|(_, next_line)| {
let next_line = next_line.trim_start();
TAGS.iter().any(|(tag, _)| next_line.starts_with(tag))
})
{
// It seems like ending this line with a jinja tag is not needed after all.
tidy_error!(
bad,
"`{}` at line {}: unneeded `{{# #}}` tag at the end of the line",
path.path().display(),
pos + 1,
);
}
continue;
}
let Some(next_line) = lines.peek().map(|(_, next_line)| next_line.trim()) else {
continue;
};
if TAGS.iter().any(|(tag, _)| next_line.starts_with(tag)) {
continue;
}
// Maybe this is a multi-line tag, let's filter it out then.
match TAGS.iter().find_map(|(tag, end_tag)| {
if line.rfind(tag).is_some() { Some(end_tag) } else { None }
}) {
None => {
// No it's not, let's error.
tidy_error!(
bad,
"`{}` at line {}: missing `{{# #}}` at the end of the line",
path.path().display(),
pos + 1,
);
}
Some(end_tag) => {
// We skip the tag.
while let Some((_, next_line)) = lines.peek() {
if next_line.contains(end_tag) {
break;
}
lines.next();
}
}
}
}
},
);
}