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);
}
},
);
}