1use std::path::Path;
4
5use fluent_syntax::ast::{Entry, Message, PatternElement};
6
7use crate::walk::{filter_dirs, walk};
8
9#[rustfmt::skip]
10const ALLOWED_CAPITALIZED_WORDS: &[&str] = &[
11 "ABI",
13 "ABIs",
14 "ADT",
15 "C",
16 "CGU",
17 "Ferris",
18 "MIR",
19 "OK",
20 "Rust",
21 "VS", ];
24
25fn filter_fluent(path: &Path) -> bool {
26 if let Some(ext) = path.extension() { ext.to_str() != Some("ftl") } else { true }
27}
28
29fn is_allowed_capitalized_word(msg: &str) -> bool {
30 ALLOWED_CAPITALIZED_WORDS.iter().any(|word| {
31 msg.strip_prefix(word)
32 .map(|tail| tail.chars().next().map(|c| c == '-' || c.is_whitespace()).unwrap_or(true))
33 .unwrap_or_default()
34 })
35}
36
37fn check_lowercase(filename: &str, contents: &str, bad: &mut bool) {
38 let (Ok(parse) | Err((parse, _))) = fluent_syntax::parser::parse(contents);
39
40 for entry in &parse.body {
41 if let Entry::Message(msg) = entry
42 && let Message { value: Some(pattern), .. } = msg
43 && let [first_pattern, ..] = &pattern.elements[..]
44 && let PatternElement::TextElement { value } = first_pattern
45 && value.chars().next().is_some_and(char::is_uppercase)
46 && !is_allowed_capitalized_word(value)
47 {
48 tidy_error!(
49 bad,
50 "{filename}: message `{value}` starts with an uppercase letter. Fix it or add it to `ALLOWED_CAPITALIZED_WORDS`"
51 );
52 }
53 }
54}
55
56pub fn check(path: &Path, bad: &mut bool) {
57 walk(
58 path,
59 |path, is_dir| filter_dirs(path) || (!is_dir && filter_fluent(path)),
60 &mut |ent, contents| {
61 check_lowercase(ent.path().to_str().unwrap(), contents, bad);
62 },
63 );
64}