tidy/
rustdoc_css_themes.rs

1//! Tidy check to make sure light and dark themes are synchronized between
2//! JS-controlled rustdoc.css and noscript.css
3
4use std::path::Path;
5
6pub fn check(librustdoc_path: &Path, bad: &mut bool) {
7    let rustdoc_css = "html/static/css/rustdoc.css";
8    let noscript_css = "html/static/css/noscript.css";
9    let rustdoc_css_contents = std::fs::read_to_string(librustdoc_path.join(rustdoc_css))
10        .unwrap_or_else(|e| panic!("failed to read librustdoc/{rustdoc_css}: {e}"));
11    let noscript_css_contents = std::fs::read_to_string(librustdoc_path.join(noscript_css))
12        .unwrap_or_else(|e| panic!("failed to read librustdoc/{noscript_css}: {e}"));
13    compare_themes_from_files(
14        "light",
15        rustdoc_css_contents.lines().enumerate().map(|(i, l)| (i + 1, l.trim())),
16        noscript_css_contents.lines().enumerate().map(|(i, l)| (i + 1, l.trim())),
17        bad,
18    );
19    compare_themes_from_files(
20        "dark",
21        rustdoc_css_contents.lines().enumerate(),
22        noscript_css_contents.lines().enumerate(),
23        bad,
24    );
25}
26
27fn compare_themes_from_files<'a>(
28    name: &str,
29    mut rustdoc_css_lines: impl Iterator<Item = (usize, &'a str)>,
30    mut noscript_css_lines: impl Iterator<Item = (usize, &'a str)>,
31    bad: &mut bool,
32) {
33    let begin_theme_pat = format!("/* Begin theme: {name}");
34    let mut found_theme = None;
35    let mut found_theme_noscript = None;
36    while let Some((rustdoc_css_line_number, rustdoc_css_line)) = rustdoc_css_lines.next() {
37        if !rustdoc_css_line.starts_with(&begin_theme_pat) {
38            continue;
39        }
40        if let Some(found_theme) = found_theme {
41            tidy_error!(
42                bad,
43                "rustdoc.css contains two {name} themes on lines {rustdoc_css_line_number} and {found_theme}",
44            );
45            return;
46        }
47        found_theme = Some(rustdoc_css_line_number);
48        while let Some((noscript_css_line_number, noscript_css_line)) = noscript_css_lines.next() {
49            if !noscript_css_line.starts_with(&begin_theme_pat) {
50                continue;
51            }
52            if let Some(found_theme_noscript) = found_theme_noscript {
53                tidy_error!(
54                    bad,
55                    "noscript.css contains two {name} themes on lines {noscript_css_line_number} and {found_theme_noscript}",
56                );
57                return;
58            }
59            found_theme_noscript = Some(noscript_css_line_number);
60            compare_themes(name, &mut rustdoc_css_lines, &mut noscript_css_lines, bad);
61        }
62    }
63}
64
65fn compare_themes<'a>(
66    name: &str,
67    rustdoc_css_lines: impl Iterator<Item = (usize, &'a str)>,
68    noscript_css_lines: impl Iterator<Item = (usize, &'a str)>,
69    bad: &mut bool,
70) {
71    let end_theme_pat = format!("/* End theme: {name}");
72    for (
73        (rustdoc_css_line_number, rustdoc_css_line),
74        (noscript_css_line_number, noscript_css_line),
75    ) in rustdoc_css_lines.zip(noscript_css_lines)
76    {
77        if noscript_css_line.starts_with(":root, :root:not([data-theme]) {")
78            && (rustdoc_css_line.starts_with(&format!(r#":root[data-theme="{name}"] {{"#))
79                || rustdoc_css_line.starts_with(&format!(
80                    r#":root[data-theme="{name}"], :root:not([data-theme]) {{"#
81                )))
82        {
83            // selectors are different between rustdoc.css and noscript.css
84            // that's why they both exist: one uses JS, the other uses media queries
85            continue;
86        }
87        if noscript_css_line.starts_with(&end_theme_pat)
88            && rustdoc_css_line.starts_with(&end_theme_pat)
89        {
90            break;
91        }
92        if rustdoc_css_line != noscript_css_line {
93            tidy_error!(
94                bad,
95                "noscript.css:{noscript_css_line_number} and rustdoc.css:{rustdoc_css_line_number} contain copies of {name} theme that are not the same",
96            );
97            eprintln!("- {noscript_css_line}");
98            eprintln!("+ {rustdoc_css_line}");
99            return;
100        }
101    }
102}