#![doc = include_str!("../README.md")]
use std::{
borrow::Cow,
collections::HashMap,
fs,
marker::PhantomPinned,
path::{Path, PathBuf},
};
macro_rules! include_mods {
($($mod:ident),*) => {
$(
mod $mod;
#[allow(unused_imports)]
pub use $mod::*;
)*
};
}
include_mods!(cache, error);
pub type Locale = json::Value;
pub(crate) type Sources = HashMap<String, PathBuf>;
pub struct Localizer<'this> {
locales: Sources,
cache: LocaleCache<'this>,
default: Option<&'static str>,
debug: bool,
_pin: PhantomPinned,
}
impl<'this> Localizer<'this> {
pub fn new(locales_path: impl AsRef<Path>) -> Self {
let locales = walkdir::WalkDir::new(locales_path)
.into_iter()
.filter_map(|de| match de {
Ok(de)
if de.file_type().is_file()
&& de.file_name().to_string_lossy().ends_with(".json") =>
{
log::trace!("Found locale: `{:?}`", de.path());
Some((
de.file_name()
.to_str()
.unwrap()
.split_once(".json")
.unwrap()
.0
.into(),
de.path().to_path_buf(),
))
}
_ => None,
})
.collect();
Self {
locales,
cache: LocaleCache::new(),
debug: false,
_pin: PhantomPinned::default(),
default: None,
}
}
#[must_use]
pub fn precache_all(mut self) -> Self {
for (s, _) in unsafe { std::mem::transmute::<_, &'_ Sources>(&self.locales) }.iter() {
self.cache.prefer_recache(s, &self.locales).unwrap();
}
self
}
#[must_use]
#[inline]
pub fn debug(mut self, enable: bool) -> Self {
self.debug = enable;
self
}
pub fn default_locale(mut self, default_locale: &'static str) -> Self {
self.default = Some(default_locale);
self.cache.1 = Some(default_locale);
self
}
pub fn localize<'w>(&mut self, tag: &'w str) -> Result<&Locale>
where
'w: 'this,
{
if self.debug {
self.cache.prefer_recache(tag, &self.locales)
} else {
self.cache.look_up_or_cache(tag, &self.locales)
}
}
pub fn localize_no_cache<'w>(&self, tag: &'w str) -> Result<Cow<Locale>>
where
'w: 'this,
{
if self.debug {
log::trace!("Running debug mode, fetching locale `{}` from disk", tag);
if let Some(locale) = self.locales.get(tag) {
log::trace!("Found locale `{}` in sources, fetching from disk.", tag);
Ok(
Cow::Owned(
json::from_reader(fs::File::open(locale)?)?
)
)
} else if let Some(default_tag) = self.default {
log::trace!("Locale `{}` missing in sources, falling back to default locale `{}`", tag,default_tag);
Ok(
Cow::Owned(
json::from_reader(fs::File::open(
self.locales.get(default_tag).ok_or(
LocaleError::FallbackToDefaultFailed
)?
)?)?
)
)
} else {
log::error!("Locale `{}` wasn't found in sources, and default was `None`. Aborted.", tag);
Err(LocaleError::SourceLookupFailed)
}
} else {
self.cache.look_up_or_fetch(tag, &self.locales)
}
}
}