first commit
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
use serde_json::{json, Value};
|
||||
use std::{env, fs, path::PathBuf};
|
||||
|
||||
fn required_string<'a>(meta: &'a Value, key: &str, file: &str) -> &'a str {
|
||||
meta.get(key)
|
||||
.and_then(Value::as_str)
|
||||
.filter(|value| !value.trim().is_empty())
|
||||
.unwrap_or_else(|| panic!("{file}: meta.{key} must be a non-empty string"))
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"));
|
||||
let lang_dir = manifest_dir.join("lang");
|
||||
println!("cargo:rerun-if-changed={}", lang_dir.display());
|
||||
|
||||
let mut files: Vec<PathBuf> = fs::read_dir(&lang_dir)
|
||||
.unwrap_or_else(|error| panic!("cannot read {}: {error}", lang_dir.display()))
|
||||
.filter_map(Result::ok)
|
||||
.map(|entry| entry.path())
|
||||
.filter(|path| path.extension().and_then(|ext| ext.to_str()) == Some("json"))
|
||||
.collect();
|
||||
files.sort();
|
||||
|
||||
if files.is_empty() {
|
||||
panic!("no language files found in {}", lang_dir.display());
|
||||
}
|
||||
|
||||
let mut assets = Vec::new();
|
||||
let mut manifest_languages = Vec::new();
|
||||
let mut has_english = false;
|
||||
|
||||
for path in files {
|
||||
println!("cargo:rerun-if-changed={}", path.display());
|
||||
let filename = path.file_name().and_then(|v| v.to_str()).expect("UTF-8 language filename");
|
||||
let stem = path.file_stem().and_then(|v| v.to_str()).expect("UTF-8 language code");
|
||||
if !stem.chars().all(|ch| ch.is_ascii_alphanumeric() || ch == '-' || ch == '_') {
|
||||
panic!("{filename}: filename may only contain ASCII letters, digits, '-' and '_'");
|
||||
}
|
||||
|
||||
let source = fs::read_to_string(&path)
|
||||
.unwrap_or_else(|error| panic!("cannot read {}: {error}", path.display()));
|
||||
let document: Value = serde_json::from_str(&source)
|
||||
.unwrap_or_else(|error| panic!("{filename}: invalid JSON: {error}"));
|
||||
let meta = document.get("meta").unwrap_or_else(|| panic!("{filename}: missing meta object"));
|
||||
let code = required_string(meta, "code", filename);
|
||||
let name = required_string(meta, "name", filename);
|
||||
let native_name = required_string(meta, "native_name", filename);
|
||||
let locale = required_string(meta, "locale", filename);
|
||||
if code != stem {
|
||||
panic!("{filename}: meta.code '{code}' must match filename '{stem}.json'");
|
||||
}
|
||||
if !document.get("translations").is_some_and(Value::is_object) {
|
||||
panic!("{filename}: translations must be a JSON object");
|
||||
}
|
||||
if code == "en" {
|
||||
has_english = true;
|
||||
}
|
||||
|
||||
manifest_languages.push(json!({
|
||||
"code": code,
|
||||
"name": name,
|
||||
"native_name": native_name,
|
||||
"locale": locale,
|
||||
"path": format!("/lang/{code}.json")
|
||||
}));
|
||||
assets.push((code.to_owned(), path));
|
||||
}
|
||||
|
||||
if !has_english {
|
||||
panic!("lang/en.json is required as the default fallback language");
|
||||
}
|
||||
|
||||
let manifest_json = serde_json::to_string(&json!({
|
||||
"default": "en",
|
||||
"languages": manifest_languages
|
||||
})).expect("serialize language manifest");
|
||||
|
||||
let mut generated = String::new();
|
||||
generated.push_str("// @generated by build.rs - do not edit.\n");
|
||||
generated.push_str(&format!("pub const LANGUAGE_MANIFEST_JSON: &str = {:?};\n", manifest_json));
|
||||
generated.push_str("pub const LANGUAGE_ASSETS: &[(&str, &str)] = &[\n");
|
||||
for (code, path) in assets {
|
||||
generated.push_str(&format!(
|
||||
" ({:?}, include_str!({:?})),\n",
|
||||
code,
|
||||
path.to_string_lossy()
|
||||
));
|
||||
}
|
||||
generated.push_str("];\n");
|
||||
|
||||
let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR"));
|
||||
fs::write(out_dir.join("languages.rs"), generated).expect("write generated languages.rs");
|
||||
}
|
||||
Reference in New Issue
Block a user