use std::collections::HashMap;
use std::path::PathBuf;
use icu_locale::Locale;
use miette::Diagnostic;
use thiserror::Error;
use wax::{BuildError, Glob, WalkEntry, WalkError};
#[derive(Diagnostic, Debug, Error)]
#[error(transparent)]
pub enum Error {
Build(#[from] BuildError),
Walk(#[from] WalkError),
#[error("Directory could not be matched by glob in macro attribute")]
NoMatchesInEntry {
path: PathBuf,
complete_match: String,
},
#[error("Glob did not match any valid files")]
NoMatchesInGlob,
#[error("Invalid locale identifier")]
InvalidLocale {
path: PathBuf,
locale: String,
source: icu_locale::ParseError,
},
}
pub fn locales(attribute: &syn::LitStr) -> Result<HashMap<Locale, PathBuf>, Error> {
let manifest_root = std::env::var("CARGO_MANIFEST_DIR").unwrap();
let attribute_glob = attribute.value();
let mut locales = HashMap::new();
let glob = Glob::new(&attribute_glob)?;
let entries = glob
.walk(&manifest_root)
.collect::<Result<Vec<WalkEntry>, WalkError>>()?;
for diagnostic in glob.diagnose() {
eprintln!("{diagnostic:?}");
}
for entry in &entries {
let captured_locale = if let Some(captured) = entry.matched().get(1) {
captured
} else {
return Err(Error::NoMatchesInEntry {
path: entry.path().to_path_buf(),
complete_match: entry.matched().complete().to_string(),
});
};
let stripped_locale = captured_locale.strip_suffix('/').unwrap_or(captured_locale);
let locale =
Locale::try_from_str(stripped_locale).map_err(|source| Error::InvalidLocale {
path: entry.path().to_path_buf(),
locale: stripped_locale.to_string(),
source,
})?;
let previous_value = locales.insert(locale, entry.path().to_path_buf());
assert!(previous_value.is_none());
}
if locales.is_empty() {
return Err(Error::NoMatchesInGlob);
}
Ok(locales)
}