diff options
Diffstat (limited to 'third_party/rust/intl-memoizer/src')
-rw-r--r-- | third_party/rust/intl-memoizer/src/concurrent.rs | 39 | ||||
-rw-r--r-- | third_party/rust/intl-memoizer/src/lib.rs | 140 |
2 files changed, 179 insertions, 0 deletions
diff --git a/third_party/rust/intl-memoizer/src/concurrent.rs b/third_party/rust/intl-memoizer/src/concurrent.rs new file mode 100644 index 0000000000..95553281b6 --- /dev/null +++ b/third_party/rust/intl-memoizer/src/concurrent.rs @@ -0,0 +1,39 @@ +use super::*; +use std::sync::Mutex; + +#[derive(Debug)] +pub struct IntlLangMemoizer { + lang: LanguageIdentifier, + map: Mutex<type_map::concurrent::TypeMap>, +} + +impl IntlLangMemoizer { + pub fn new(lang: LanguageIdentifier) -> Self { + Self { + lang, + map: Mutex::new(type_map::concurrent::TypeMap::new()), + } + } + + pub fn with_try_get<I, R, U>(&self, args: I::Args, cb: U) -> Result<R, I::Error> + where + Self: Sized, + I: Memoizable + Sync + Send + 'static, + I::Args: Send + Sync + 'static, + U: FnOnce(&I) -> R, + { + let mut map = self.map.lock().unwrap(); + let cache = map + .entry::<HashMap<I::Args, I>>() + .or_insert_with(HashMap::new); + + let e = match cache.entry(args.clone()) { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => { + let val = I::construct(self.lang.clone(), args)?; + entry.insert(val) + } + }; + Ok(cb(&e)) + } +} diff --git a/third_party/rust/intl-memoizer/src/lib.rs b/third_party/rust/intl-memoizer/src/lib.rs new file mode 100644 index 0000000000..8205616a87 --- /dev/null +++ b/third_party/rust/intl-memoizer/src/lib.rs @@ -0,0 +1,140 @@ +use std::cell::RefCell; +use std::collections::hash_map::Entry; +use std::collections::HashMap; +use std::hash::Hash; +use std::rc::{Rc, Weak}; +use unic_langid::LanguageIdentifier; + +pub mod concurrent; + +pub trait Memoizable { + type Args: 'static + Eq + Hash + Clone; + type Error; + fn construct(lang: LanguageIdentifier, args: Self::Args) -> Result<Self, Self::Error> + where + Self: std::marker::Sized; +} + +#[derive(Debug)] +pub struct IntlLangMemoizer { + lang: LanguageIdentifier, + map: RefCell<type_map::TypeMap>, +} + +impl IntlLangMemoizer { + pub fn new(lang: LanguageIdentifier) -> Self { + Self { + lang, + map: RefCell::new(type_map::TypeMap::new()), + } + } + + pub fn with_try_get<I, R, U>(&self, args: I::Args, cb: U) -> Result<R, I::Error> + where + Self: Sized, + I: Memoizable + 'static, + U: FnOnce(&I) -> R, + { + let mut map = self + .map + .try_borrow_mut() + .expect("Cannot use memoizer reentrantly"); + let cache = map + .entry::<HashMap<I::Args, I>>() + .or_insert_with(HashMap::new); + + let e = match cache.entry(args.clone()) { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => { + let val = I::construct(self.lang.clone(), args)?; + entry.insert(val) + } + }; + Ok(cb(&e)) + } +} + +#[derive(Default)] +pub struct IntlMemoizer { + map: HashMap<LanguageIdentifier, Weak<IntlLangMemoizer>>, +} + +impl IntlMemoizer { + pub fn get_for_lang(&mut self, lang: LanguageIdentifier) -> Rc<IntlLangMemoizer> { + match self.map.entry(lang.clone()) { + Entry::Vacant(empty) => { + let entry = Rc::new(IntlLangMemoizer::new(lang)); + empty.insert(Rc::downgrade(&entry)); + entry + } + Entry::Occupied(mut entry) => { + if let Some(entry) = entry.get().upgrade() { + entry + } else { + let e = Rc::new(IntlLangMemoizer::new(lang)); + entry.insert(Rc::downgrade(&e)); + e + } + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use fluent_langneg::{negotiate_languages, NegotiationStrategy}; + use intl_pluralrules::{PluralCategory, PluralRuleType, PluralRules as IntlPluralRules}; + + struct PluralRules(pub IntlPluralRules); + + impl PluralRules { + pub fn new( + lang: LanguageIdentifier, + pr_type: PluralRuleType, + ) -> Result<Self, &'static str> { + let default_lang: LanguageIdentifier = "en".parse().unwrap(); + let pr_lang = negotiate_languages( + &[lang], + &IntlPluralRules::get_locales(pr_type), + Some(&default_lang), + NegotiationStrategy::Lookup, + )[0] + .clone(); + + Ok(Self(IntlPluralRules::create(pr_lang, pr_type)?)) + } + } + + impl Memoizable for PluralRules { + type Args = (PluralRuleType,); + type Error = &'static str; + fn construct(lang: LanguageIdentifier, args: Self::Args) -> Result<Self, Self::Error> { + Self::new(lang, args.0) + } + } + + #[test] + fn it_works() { + let lang: LanguageIdentifier = "en".parse().unwrap(); + + let mut memoizer = IntlMemoizer::default(); + { + let en_memoizer = memoizer.get_for_lang(lang.clone()); + + let result = en_memoizer + .with_try_get::<PluralRules, _, _>((PluralRuleType::CARDINAL,), |cb| cb.0.select(5)) + .unwrap(); + assert_eq!(result, Ok(PluralCategory::OTHER)); + } + + { + let en_memoizer = memoizer.get_for_lang(lang.clone()); + + let result = en_memoizer + .with_try_get::<PluralRules, _, _>((PluralRuleType::CARDINAL,), |cb| cb.0.select(5)) + .unwrap(); + assert_eq!(result, Ok(PluralCategory::OTHER)); + } + } +} |