1 //! A crate for generating plural rule operands from numberical input.
2 //!
3 //! This crate generates plural operands according to the specifications outlined at [Unicode's website](http://unicode.org/reports/tr35/tr35-numbers.html#Operands).
4 //!
5 //! Input is supported for int, float, and &str.
6 //!
7 //! # Examples
8 //!
9 //! Plural rules example for Polish
10 //!
11 //! ```
12 //! use intl_pluralrules::{PluralRules, PluralRuleType, PluralCategory};
13 //! use unic_langid::LanguageIdentifier;
14 //!
15 //! let langid: LanguageIdentifier = "pl".parse().expect("Parsing failed.");
16 //!
17 //! assert!(PluralRules::get_locales(PluralRuleType::CARDINAL).contains(&langid));
18 //!
19 //! let pr = PluralRules::create(langid.clone(), PluralRuleType::CARDINAL).unwrap();
20 //! assert_eq!(pr.select(1), Ok(PluralCategory::ONE));
21 //! assert_eq!(pr.select("3"), Ok(PluralCategory::FEW));
22 //! assert_eq!(pr.select(12), Ok(PluralCategory::MANY));
23 //! assert_eq!(pr.select("5.0"), Ok(PluralCategory::OTHER));
24 //!
25 //! assert_eq!(pr.get_locale(), &langid);
26 //! ```
27 
28 /// A public AST module for plural rule representations.
29 pub mod operands;
30 #[cfg_attr(tarpaulin, skip)]
31 mod rules;
32 
33 use std::convert::TryInto;
34 
35 use unic_langid::LanguageIdentifier;
36 
37 use crate::operands::PluralOperands;
38 use crate::rules::*;
39 
40 /// A public enum for handling the plural category.
41 /// Each plural category will vary, depending on the language that is being used and whether that language has that plural category.
42 #[derive(Debug, Eq, PartialEq)]
43 pub enum PluralCategory {
44     ZERO,
45     ONE,
46     TWO,
47     FEW,
48     MANY,
49     OTHER,
50 }
51 
52 /// A public enum for handling plural type.
53 #[derive(Copy, Clone, Hash, PartialEq, Eq)]
54 pub enum PluralRuleType {
55     /// Ordinal numbers express position or rank in a sequence. [More about oridinal numbers](https://en.wikipedia.org/wiki/Ordinal_number_(linguistics))
56     ORDINAL,
57     /// Cardinal numbers are natural numbers. [More about cardinal numbers](https://en.wikipedia.org/wiki/Cardinal_number)
58     CARDINAL,
59 }
60 
61 // pub use rules::PluralRuleType;
62 /// CLDR_VERSION is the version of CLDR extracted from the file used to generate rules.rs.
63 pub use crate::rules::CLDR_VERSION;
64 
65 /// The main structure for selecting plural rules.
66 ///
67 /// # Examples
68 ///
69 /// ```
70 /// use intl_pluralrules::{PluralRules, PluralRuleType, PluralCategory};
71 /// use unic_langid::LanguageIdentifier;
72 ///
73 /// let langid: LanguageIdentifier = "naq".parse().expect("Parsing failed.");
74 /// let pr_naq = PluralRules::create(langid, PluralRuleType::CARDINAL).unwrap();
75 /// assert_eq!(pr_naq.select(1), Ok(PluralCategory::ONE));
76 /// assert_eq!(pr_naq.select("2"), Ok(PluralCategory::TWO));
77 /// assert_eq!(pr_naq.select(5.0), Ok(PluralCategory::OTHER));
78 /// ```
79 #[derive(Clone)]
80 pub struct PluralRules {
81     locale: LanguageIdentifier,
82     function: PluralRule,
83 }
84 
85 impl PluralRules {
86     /// Returns an instance of PluralRules.
87     ///
88     /// # Examples
89     /// ```
90     /// use intl_pluralrules::{PluralRules, PluralRuleType, PluralCategory};
91     /// use unic_langid::LanguageIdentifier;
92     ///
93     /// let langid: LanguageIdentifier = "naq".parse().expect("Parsing failed.");
94     /// let pr_naq = PluralRules::create(langid, PluralRuleType::CARDINAL);
95     /// assert_eq!(pr_naq.is_ok(), !pr_naq.is_err());
96     ///
97     /// let langid: LanguageIdentifier = "xx".parse().expect("Parsing failed.");
98     /// let pr_broken = PluralRules::create(langid, PluralRuleType::CARDINAL);
99     /// assert_eq!(pr_broken.is_err(), !pr_broken.is_ok());
100     /// ```
create<L: Into<LanguageIdentifier>>( langid: L, prt: PluralRuleType, ) -> Result<Self, &'static str>101     pub fn create<L: Into<LanguageIdentifier>>(
102         langid: L,
103         prt: PluralRuleType,
104     ) -> Result<Self, &'static str> {
105         let langid = langid.into();
106         let returned_rule = match prt {
107             PluralRuleType::CARDINAL => {
108                 let idx = rules::PRS_CARDINAL.binary_search_by_key(&&langid, |(l, _)| l);
109                 idx.map(|idx| rules::PRS_CARDINAL[idx].1)
110             }
111             PluralRuleType::ORDINAL => {
112                 let idx = rules::PRS_ORDINAL.binary_search_by_key(&&langid, |(l, _)| l);
113                 idx.map(|idx| rules::PRS_ORDINAL[idx].1)
114             }
115         };
116         match returned_rule {
117             Ok(returned_rule) => Ok(Self {
118                 locale: langid,
119                 function: returned_rule,
120             }),
121             Err(_) => Err("unknown locale"),
122         }
123     }
124 
125     /// Returns a result of the plural category for the given input.
126     ///
127     /// If the input is not numeric.
128     ///
129     /// # Examples
130     /// ```
131     /// use intl_pluralrules::{PluralRules, PluralRuleType, PluralCategory};
132     /// use unic_langid::LanguageIdentifier;
133     ///
134     /// let langid: LanguageIdentifier = "naq".parse().expect("Parsing failed.");
135     /// let pr_naq = PluralRules::create(langid, PluralRuleType::CARDINAL).unwrap();
136     /// assert_eq!(pr_naq.select(1), Ok(PluralCategory::ONE));
137     /// assert_eq!(pr_naq.select(2), Ok(PluralCategory::TWO));
138     /// assert_eq!(pr_naq.select(5), Ok(PluralCategory::OTHER));
139     /// ```
select<N: TryInto<PluralOperands>>( &self, number: N, ) -> Result<PluralCategory, &'static str>140     pub fn select<N: TryInto<PluralOperands>>(
141         &self,
142         number: N,
143     ) -> Result<PluralCategory, &'static str> {
144         let ops = number.try_into();
145         let pr = self.function;
146         match ops {
147             Ok(ops) => Ok(pr(&ops)),
148             Err(_) => Err("Argument can not be parsed to operands."),
149         }
150     }
151 
152     /// Returns a list of the available locales.
153     ///
154     /// # Examples
155     /// ```
156     /// use intl_pluralrules::{PluralRules, PluralRuleType};
157     ///
158     /// assert_eq!(
159     ///     PluralRules::get_locales(PluralRuleType::CARDINAL).is_empty(),
160     ///     false
161     /// );
162     /// ```
get_locales(prt: PluralRuleType) -> Vec<LanguageIdentifier>163     pub fn get_locales(prt: PluralRuleType) -> Vec<LanguageIdentifier> {
164         let prs = match prt {
165             PluralRuleType::CARDINAL => rules::PRS_CARDINAL,
166             PluralRuleType::ORDINAL => rules::PRS_ORDINAL,
167         };
168         prs.iter().map(|(l, _)| l.clone()).collect()
169     }
170 
171     /// Returns the locale name for this PluralRule instance.
172     ///
173     /// # Examples
174     /// ```
175     /// use intl_pluralrules::{PluralRules, PluralRuleType};
176     /// use unic_langid::LanguageIdentifier;
177     ///
178     /// let langid: LanguageIdentifier = "naq".parse().expect("Parsing failed.");
179     /// let pr_naq = PluralRules::create(langid.clone(), PluralRuleType::CARDINAL).unwrap();
180     /// assert_eq!(pr_naq.get_locale(), &langid);
181     /// ```
get_locale(&self) -> &LanguageIdentifier182     pub fn get_locale(&self) -> &LanguageIdentifier {
183         &self.locale
184     }
185 }
186 
187 #[cfg(test)]
188 mod tests {
189     use super::{PluralCategory, PluralRuleType, PluralRules, CLDR_VERSION};
190     use unic_langid::LanguageIdentifier;
191 
192     #[test]
cardinals_test()193     fn cardinals_test() {
194         let langid: LanguageIdentifier = "naq".parse().expect("Parsing failed.");
195         let pr_naq = PluralRules::create(langid, PluralRuleType::CARDINAL).unwrap();
196         assert_eq!(pr_naq.select(1), Ok(PluralCategory::ONE));
197         assert_eq!(pr_naq.select(2), Ok(PluralCategory::TWO));
198         assert_eq!(pr_naq.select(5), Ok(PluralCategory::OTHER));
199 
200         let langid: LanguageIdentifier = "xx".parse().expect("Parsing failed.");
201         let pr_broken = PluralRules::create(langid, PluralRuleType::CARDINAL);
202         assert_eq!(pr_broken.is_err(), !pr_broken.is_ok());
203     }
204 
205     #[test]
ordinals_rules()206     fn ordinals_rules() {
207         let langid: LanguageIdentifier = "uk".parse().expect("Parsing failed.");
208         let pr_naq = PluralRules::create(langid, PluralRuleType::ORDINAL).unwrap();
209         assert_eq!(pr_naq.select(33), Ok(PluralCategory::FEW));
210         assert_eq!(pr_naq.select(113), Ok(PluralCategory::OTHER));
211     }
212 
213     #[test]
version_test()214     fn version_test() {
215         assert_eq!(CLDR_VERSION, 36);
216     }
217 
218     #[test]
locale_test()219     fn locale_test() {
220         assert_eq!(
221             PluralRules::get_locales(PluralRuleType::CARDINAL).is_empty(),
222             false
223         );
224     }
225 }
226