| @@ -71,9 +71,9 @@ pub struct Audit { |
| 71 | pub link_schemes: BTreeMap<String, Tally>, |
71 | pub link_schemes: BTreeMap<String, Tally>, |
| 72 | } |
72 | } |
| 73 | |
73 | |
| 74 | /// Names the implementation understands, so the census can flag everything else. These |
74 | /// Names the implementation reads by name, so the census can mark everything else. These |
| 75 | /// are the *recognized* sets, not the supported ones: `INCLUDE` is recognized (it is |
75 | /// are the *recognized* sets, not the supported ones: `INCLUDE` is recognized and |
| 76 | /// deliberately inert) while an unlisted keyword is a genuine blind spot. |
76 | /// deliberately inert. |
| 77 | const KNOWN_KEYWORDS: &[&str] = &[ |
77 | const KNOWN_KEYWORDS: &[&str] = &[ |
| 78 | "TITLE", "AUTHOR", "DATE", "EMAIL", "LANGUAGE", "OPTIONS", "FILETAGS", "DESCRIPTION", |
78 | "TITLE", "AUTHOR", "DATE", "EMAIL", "LANGUAGE", "OPTIONS", "FILETAGS", "DESCRIPTION", |
| 79 | "KEYWORDS", "CAPTION", "NAME", "ATTR_HTML", "RESULTS", "TBLFM", "INCLUDE", "TODO", |
79 | "KEYWORDS", "CAPTION", "NAME", "ATTR_HTML", "RESULTS", "TBLFM", "INCLUDE", "TODO", |
| @@ -97,20 +97,36 @@ const KNOWN_SCHEMES: &[&str] = &[ |
| 97 | "relative", |
97 | "relative", |
| 98 | ]; |
98 | ]; |
| 99 | |
99 | |
| |
100 | /// The census markers. A name with dedicated handling carries none; `PASS_THROUGH` says |
| |
101 | /// nothing reads the name but it works anyway; `UNKNOWN` is the blind-spot signal. |
| |
102 | const HANDLED: &str = ""; |
| |
103 | const PASS_THROUGH: &str = "\u{2014}"; |
| |
104 | const UNKNOWN: &str = "???"; |
| |
105 | |
| 100 | impl Audit { |
106 | impl Audit { |
| 101 | /// Is this name one the implementation recognizes? |
107 | /// How the report marks a census name: the recognized set to look the name up in, |
| 102 | pub fn is_known(kind: Census, name: &str) -> bool { |
108 | /// and what an unlisted name means for that census. |
| 103 | let known = match kind { |
109 | pub fn marker(kind: Census, name: &str) -> &'static str { |
| 104 | Census::Keyword => KNOWN_KEYWORDS, |
110 | let (known, unlisted) = match kind { |
| |
111 | // An unlisted keyword is not a blind spot. Reaching templates as |
| |
112 | // `page.keywords.<name>` is the designed behaviour, so every keyword name |
| |
113 | // works; what the census reports is which ones have dedicated handling. |
| |
114 | // `LEDE`, read by the shipped docs theme's template, was the case that made |
| |
115 | // this concrete: `???` on it was a claim about orgo's source, not a gap. |
| |
116 | Census::Keyword => (KNOWN_KEYWORDS, PASS_THROUGH), |
| 105 | // Every block name renders, and renders as org renders it: the names in |
117 | // Every block name renders, and renders as org renders it: the names in |
| 106 | // `block_construct` through dedicated handling, every other name as a special |
118 | // `block_construct` through dedicated handling, every other name as a special |
| 107 | // block — a div carrying the name, holding parsed org, which is exactly what |
119 | // block — a div carrying the name, holding parsed org, which is exactly what |
| 108 | // org's exporter emits. No block name is a blind spot. |
120 | // org's exporter emits. No block name is a blind spot. |
| 109 | Census::Block => return true, |
121 | Census::Block => return HANDLED, |
| 110 | Census::Drawer => KNOWN_DRAWERS, |
122 | Census::Drawer => (KNOWN_DRAWERS, UNKNOWN), |
| 111 | Census::Scheme => KNOWN_SCHEMES, |
123 | Census::Scheme => (KNOWN_SCHEMES, UNKNOWN), |
| 112 | }; |
124 | }; |
| 113 | known.iter().any(|k| k.eq_ignore_ascii_case(name)) |
125 | if known.iter().any(|k| k.eq_ignore_ascii_case(name)) { |
| |
126 | HANDLED |
| |
127 | } else { |
| |
128 | unlisted |
| |
129 | } |
| 114 | } |
130 | } |
| 115 | } |
131 | } |
| 116 | |
132 | |
| @@ -644,13 +660,9 @@ pub fn report(audit: &Audit) -> String { |
| 644 | names.sort_by(|a, b| b.1.occurrences.cmp(&a.1.occurrences).then(a.0.cmp(b.0))); |
660 | names.sort_by(|a, b| b.1.occurrences.cmp(&a.1.occurrences).then(a.0.cmp(b.0))); |
| 645 | out.push_str(&format!("\n{title}\n")); |
661 | out.push_str(&format!("\n{title}\n")); |
| 646 | for (name, tally) in names { |
662 | for (name, tally) in names { |
| 647 | let flag = if Audit::is_known(kind, name) { |
| |
| 648 | " " |
| |
| 649 | } else { |
| |
| 650 | "??? " |
| |
| 651 | }; |
| |
| 652 | out.push_str(&format!( |
663 | out.push_str(&format!( |
| 653 | "{flag}{:<32} {:>8} {:>7} {}\n", |
664 | "{:<4}{:<32} {:>8} {:>7} {}\n", |
| |
665 | Audit::marker(kind, name), |
| 654 | name, |
666 | name, |
| 655 | tally.occurrences, |
667 | tally.occurrences, |
| 656 | tally.files, |
668 | tally.files, |
| @@ -658,6 +670,10 @@ pub fn report(audit: &Audit) -> String { |
| 658 | )); |
670 | )); |
| 659 | } |
671 | } |
| 660 | } |
672 | } |
| 661 | out.push_str("\n`???` marks a name the implementation does not recognize at all.\n"); |
673 | out.push_str( |
| |
674 | "\n`???` marks a name the implementation does not recognize at all.\n\ |
| |
675 | `\u{2014}` marks a keyword with no dedicated handling; it reaches templates \ |
| |
676 | as `page.keywords.<name>`.\n", |
| |
677 | ); |
| 662 | out |
678 | out |
| 663 | } |
679 | } |