1 use std::cell::RefCell;
2 use std::collections::hash_map::Entry;
3 use std::collections::HashMap;
4 use std::hash::Hash;
5 use std::rc::{Rc, Weak};
6 use unic_langid::LanguageIdentifier;
7 
8 pub mod concurrent;
9 
10 pub trait Memoizable {
11     type Args: 'static + Eq + Hash + Clone;
12     type Error;
construct(lang: LanguageIdentifier, args: Self::Args) -> Result<Self, Self::Error> where Self: std::marker::Sized13     fn construct(lang: LanguageIdentifier, args: Self::Args) -> Result<Self, Self::Error>
14     where
15         Self: std::marker::Sized;
16 }
17 
18 #[derive(Debug)]
19 pub struct IntlLangMemoizer {
20     lang: LanguageIdentifier,
21     map: RefCell<type_map::TypeMap>,
22 }
23 
24 impl IntlLangMemoizer {
new(lang: LanguageIdentifier) -> Self25     pub fn new(lang: LanguageIdentifier) -> Self {
26         Self {
27             lang,
28             map: RefCell::new(type_map::TypeMap::new()),
29         }
30     }
31 
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,32     pub fn with_try_get<I, R, U>(&self, args: I::Args, cb: U) -> Result<R, I::Error>
33     where
34         Self: Sized,
35         I: Memoizable + 'static,
36         U: FnOnce(&I) -> R,
37     {
38         let mut map = self
39             .map
40             .try_borrow_mut()
41             .expect("Cannot use memoizer reentrantly");
42         let cache = map
43             .entry::<HashMap<I::Args, I>>()
44             .or_insert_with(HashMap::new);
45 
46         let e = match cache.entry(args.clone()) {
47             Entry::Occupied(entry) => entry.into_mut(),
48             Entry::Vacant(entry) => {
49                 let val = I::construct(self.lang.clone(), args)?;
50                 entry.insert(val)
51             }
52         };
53         Ok(cb(&e))
54     }
55 }
56 
57 #[derive(Default)]
58 pub struct IntlMemoizer {
59     map: HashMap<LanguageIdentifier, Weak<IntlLangMemoizer>>,
60 }
61 
62 impl IntlMemoizer {
get_for_lang(&mut self, lang: LanguageIdentifier) -> Rc<IntlLangMemoizer>63     pub fn get_for_lang(&mut self, lang: LanguageIdentifier) -> Rc<IntlLangMemoizer> {
64         match self.map.entry(lang.clone()) {
65             Entry::Vacant(empty) => {
66                 let entry = Rc::new(IntlLangMemoizer::new(lang));
67                 empty.insert(Rc::downgrade(&entry));
68                 entry
69             }
70             Entry::Occupied(mut entry) => {
71                 if let Some(entry) = entry.get().upgrade() {
72                     entry
73                 } else {
74                     let e = Rc::new(IntlLangMemoizer::new(lang));
75                     entry.insert(Rc::downgrade(&e));
76                     e
77                 }
78             }
79         }
80     }
81 }
82 
83 #[cfg(test)]
84 mod tests {
85     use super::*;
86     use fluent_langneg::{negotiate_languages, NegotiationStrategy};
87     use intl_pluralrules::{PluralCategory, PluralRuleType, PluralRules as IntlPluralRules};
88 
89     struct PluralRules(pub IntlPluralRules);
90 
91     impl PluralRules {
new( lang: LanguageIdentifier, pr_type: PluralRuleType, ) -> Result<Self, &'static str>92         pub fn new(
93             lang: LanguageIdentifier,
94             pr_type: PluralRuleType,
95         ) -> Result<Self, &'static str> {
96             let default_lang: LanguageIdentifier = "en".parse().unwrap();
97             let pr_lang = negotiate_languages(
98                 &[lang],
99                 &IntlPluralRules::get_locales(pr_type),
100                 Some(&default_lang),
101                 NegotiationStrategy::Lookup,
102             )[0]
103             .clone();
104 
105             Ok(Self(IntlPluralRules::create(pr_lang, pr_type)?))
106         }
107     }
108 
109     impl Memoizable for PluralRules {
110         type Args = (PluralRuleType,);
111         type Error = &'static str;
construct(lang: LanguageIdentifier, args: Self::Args) -> Result<Self, Self::Error>112         fn construct(lang: LanguageIdentifier, args: Self::Args) -> Result<Self, Self::Error> {
113             Self::new(lang, args.0)
114         }
115     }
116 
117     #[test]
it_works()118     fn it_works() {
119         let lang: LanguageIdentifier = "en".parse().unwrap();
120 
121         let mut memoizer = IntlMemoizer::default();
122         {
123             let en_memoizer = memoizer.get_for_lang(lang.clone());
124 
125             let result = en_memoizer
126                 .with_try_get::<PluralRules, _, _>((PluralRuleType::CARDINAL,), |cb| cb.0.select(5))
127                 .unwrap();
128             assert_eq!(result, Ok(PluralCategory::OTHER));
129         }
130 
131         {
132             let en_memoizer = memoizer.get_for_lang(lang.clone());
133 
134             let result = en_memoizer
135                 .with_try_get::<PluralRules, _, _>((PluralRuleType::CARDINAL,), |cb| cb.0.select(5))
136                 .unwrap();
137             assert_eq!(result, Ok(PluralCategory::OTHER));
138         }
139     }
140 }
141