use std::{
env,
fs::File,
io::{BufRead, BufReader, Result, Write},
path::Path,
};
fn main() -> Result<()> {
generate_consts(
"Name",
"src/consts/names.txt",
&[
("x-", "names_generated.x_.rs"),
("vnd.", "names_generated.vnd.rs"),
("", "names_generated.rs"),
],
)?;
generate_consts(
"Value",
"src/consts/values.txt",
&[("", "values_generated.rs")],
)?;
Ok(())
}
fn generate_consts(ty: &str, input: &str, prefixes: &[(&str, &str)]) -> Result<()> {
let out_dir = env::var_os("OUT_DIR").unwrap();
let mut prefixes = prefixes
.iter()
.map(|(pf, file)| {
let dest_path = Path::new(&out_dir).join(file);
let out = File::create(dest_path).unwrap();
(pf, out)
})
.collect::<Vec<_>>();
let file = File::open(input)?;
for line in BufReader::new(file).lines().flatten() {
let (ident, name) = if let Some(pair) = line.split_once('=') {
pair
} else {
(line.as_str(), line.as_str())
};
if let Some((&pf, out)) = prefixes.iter_mut().find(|(&pf, _)| ident.starts_with(pf)) {
let ident = upper_snake_case(ident.trim_start_matches(pf));
writeln!(out, "/// `{}`", name)?;
writeln!(
out,
"pub const {}: crate::{} = crate::{}::new_unchecked(\"{}\");",
ident, ty, ty, name
)?;
}
}
println!("cargo:rerun-if-changed={}", input);
Ok(())
}
fn upper_snake_case(s: &str) -> String {
let s = s
.split_inclusive(char::is_uppercase)
.map(|chunk| {
if chunk.ends_with(char::is_uppercase) {
let prefix = chunk.trim_end_matches(char::is_uppercase);
if prefix.ends_with(char::is_lowercase) {
return format!("{}_{}", prefix, chunk.split_at(chunk.len() - 1).1);
}
}
chunk.to_string()
})
.collect::<String>()
.replace('+', "_plus")
.replace(|c| !char::is_alphanumeric(c), "_")
.to_ascii_uppercase();
if s.starts_with(char::is_numeric) {
format!("_{}", s)
} else {
s
}
}