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