use std::{collections::BTreeSet, fs, path::Path};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct LocaleMetadata {
pub tag: String,
pub native_name: String,
}
pub fn load_locale_manifest(path: &Path) -> Result<Vec<LocaleMetadata>, String> {
let source =
fs::read_to_string(path).map_err(|error| format!("{}: {error}", path.display()))?;
let value: toml::Table = source
.parse()
.map_err(|error| format!("{}: {error}", path.display()))?;
let entries = value
.get("locale")
.and_then(toml::Value::as_array)
.ok_or_else(|| format!("{}: expected ordered [[locale]] entries", path.display()))?;
if entries.is_empty() {
return Err(format!(
"{}: at least one [[locale]] entry is required",
path.display()
));
}
let mut tags = BTreeSet::new();
entries
.iter()
.map(|entry| {
let tag = entry
.get("tag")
.and_then(toml::Value::as_str)
.ok_or_else(|| "each [[locale]] needs a string `tag`".to_owned())?;
let native_name = entry
.get("native_name")
.and_then(toml::Value::as_str)
.ok_or_else(|| format!("locale `{tag}` needs a string `native_name`"))?;
let parsed = crate::Locale::parse(tag).map_err(|error| error.to_string())?;
if parsed.to_string() != tag {
return Err(format!(
"locale tag `{tag}` must use canonical BCP 47 casing"
));
}
if native_name.trim().is_empty() {
return Err(format!("locale `{tag}` has an empty native name"));
}
if !tags.insert(tag.to_owned()) {
return Err(format!("duplicate locale `{tag}`"));
}
Ok(LocaleMetadata {
tag: tag.to_owned(),
native_name: native_name.to_owned(),
})
})
.collect()
}