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
//! Checks that all Flunt files have messages in alphabetical order

use crate::walk::{filter_dirs, walk};
use std::{fs::OpenOptions, io::Write, path::Path};

use regex::Regex;

lazy_static::lazy_static! {
    static ref MESSAGE: Regex = Regex::new(r#"(?m)^([a-zA-Z0-9_]+)\s*=\s*"#).unwrap();
}

fn filter_fluent(path: &Path) -> bool {
    if let Some(ext) = path.extension() { ext.to_str() != Some("ftl") } else { true }
}

fn check_alphabetic(filename: &str, fluent: &str, bad: &mut bool) {
    let mut matches = MESSAGE.captures_iter(fluent).peekable();
    while let Some(m) = matches.next() {
        if let Some(next) = matches.peek() {
            let name = m.get(1).unwrap();
            let next = next.get(1).unwrap();
            if name.as_str() > next.as_str() {
                tidy_error!(
                    bad,
                    "{filename}: message `{}` appears before `{}`, but is alphabetically later than it
run `./x.py test tidy --bless` to sort the file correctly",
                    name.as_str(),
                    next.as_str()
                );
            }
        } else {
            break;
        }
    }
}

fn sort_messages(fluent: &str) -> String {
    let mut chunks = vec![];
    let mut cur = String::new();
    for line in fluent.lines() {
        if MESSAGE.is_match(line) {
            chunks.push(std::mem::take(&mut cur));
        }
        cur += line;
        cur.push('\n');
    }
    chunks.push(cur);
    chunks.sort();
    let mut out = chunks.join("");
    out = out.trim().to_string();
    out.push('\n');
    out
}

pub fn check(path: &Path, bless: bool, bad: &mut bool) {
    walk(
        path,
        |path, is_dir| filter_dirs(path) || (!is_dir && filter_fluent(path)),
        &mut |ent, contents| {
            if bless {
                let sorted = sort_messages(contents);
                if sorted != contents {
                    let mut f =
                        OpenOptions::new().write(true).truncate(true).open(ent.path()).unwrap();
                    f.write(sorted.as_bytes()).unwrap();
                }
            } else {
                check_alphabetic(ent.path().to_str().unwrap(), contents, bad);
            }
        },
    );
}