├── .gitignore ├── .travis.yml ├── Cargo.toml ├── README.md ├── cldr_pluralrules_parser ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── README.md ├── benches │ └── parser.rs ├── src │ ├── ast.rs │ ├── lib.rs │ └── parser.rs └── tests │ └── conditions.rs ├── intl_pluralrules ├── .cargo │ └── config ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── benches │ └── pluralrules.rs ├── cldr_data │ ├── cldr_pluralrules_cardinals.json │ └── cldr_pluralrules_ordinals.json ├── src │ ├── lib.rs │ ├── operands.rs │ └── rules.rs └── tests │ └── operands.rs └── make_pluralrules ├── .cargo └── config ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── README.md ├── src ├── lib.rs ├── main.rs └── parser │ ├── gen_pr.rs │ ├── gen_rs.rs │ ├── mod.rs │ ├── plural_category.rs │ └── resource.rs └── tests ├── fixtures ├── cldr_pluralrules_33.rs ├── cldr_pluralrules_cardinals_33.json ├── cldr_pluralrules_ordinals_33.json ├── cldr_pluralrules_within_test.json └── cldr_pluralrules_within_test.rs └── test.rs /.gitignore: -------------------------------------------------------------------------------- 1 | **/target 2 | **/Cargo.lock 3 | **/*.rs.bk 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: required 3 | dist: trusty 4 | addons: 5 | apt: 6 | packages: 7 | - libssl-dev 8 | cache: cargo 9 | rust: 10 | - stable 11 | - beta 12 | - nightly 13 | matrix: 14 | allow_failures: 15 | - rust: nightly 16 | 17 | before_cache: | 18 | if [[ "$TRAVIS_RUST_VERSION" == stable ]]; then 19 | cargo install cargo-tarpaulin -f 20 | fi 21 | 22 | script: 23 | - cargo clean 24 | - cargo build 25 | - cargo test 26 | 27 | after_success: | 28 | if [[ "$TRAVIS_RUST_VERSION" == stable ]]; then 29 | cargo tarpaulin --ciserver travis-ci --coveralls $TRAVIS_JOB_ID 30 | fi 31 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "cldr_pluralrules_parser", 4 | "make_pluralrules", 5 | "intl_pluralrules" 6 | ] 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CLDR Plural Rules [![Build Status](https://travis-ci.org/zbraniecki/pluralrules.svg?branch=master)](https://travis-ci.org/zbraniecki/pluralrules) [![Coverage Status](https://coveralls.io/repos/github/zbraniecki/pluralrules/badge.svg?branch=master)](https://coveralls.io/github/zbraniecki/pluralrules?branch=master) 2 | 3 | `cldr_pluralrules` is a collection of Rust crates for identifying the plural rule, according to [CLDR](https://github.com/unicode-cldr/cldr-core/blob/master/supplemental/plurals.json), for a given number. 4 | 5 | The crates perform the following functions: 6 | 7 | ## intl_pluralrules [![crates.io](https://img.shields.io/crates/v/intl_pluralrules.svg)](https://crates.io/crates/intl_pluralrules) 8 | 9 | 10 | This library returns the plural rule given numeric input. 11 | 12 | ## cldr_pluralrules_parser [![crates.io](https://img.shields.io/crates/v/cldr_pluralrules_parser.svg)](https://crates.io/crates/cldr_pluralrules_parser) 13 | 14 | 15 | This library creates an AST from plural rules, according to [CLDR plural rule syntax](https://unicode.org/reports/tr35/tr35-numbers.html#Plural_rules_syntax). 16 | 17 | ## make_pluralrules [![crates.io](https://img.shields.io/crates/v/make_pluralrules.svg)](https://crates.io/crates/make_pluralrules) 18 | 19 | 20 | This executable generates a Rust file with a public function representation of CLDR plural rules from a specified source. 21 | 22 | ## Status 23 | 24 | ### cldr_pluralrules_parser 25 | 26 | The parser is fully compliant with UTS #35 version 33. 27 | 28 | ### make_pluralrules 29 | 30 | Generates all cardinal plural rules for Rust 1.31 and above, based on CLDR 33. 31 | 32 | ### intl_pluralrules 33 | 34 | Currently supports cardinal plural rules for all languages from CLDR 35. 35 | 36 | Get Involved 37 | ------------ 38 | 39 | `cldr_pluralrules` is open-source, licensed under the Apache License, Version 2.0. We encourage everyone to take a look at our code and we'll listen to your feedback. 40 | -------------------------------------------------------------------------------- /cldr_pluralrules_parser/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Unreleased 4 | 5 | - … 6 | 7 | ## cldr_pluralrules_parser 2.0.0 (November 13, 2019) 8 | 9 | - Update to `nom` 5.0. 10 | - Add Samples to the AST. 11 | - Move `Operand` to be an enum of known values. 12 | - Update to Rust 2018. 13 | 14 | ## cldr_pluralrules_parser 1.0.1 (January 7, 2019) 15 | 16 | - Update to Rust 2018. 17 | 18 | ## cldr_pluralrules_parser 1.0.0 (July 25, 2018) 19 | 20 | - Initial stable release. 21 | -------------------------------------------------------------------------------- /cldr_pluralrules_parser/Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "cldr_pluralrules_parser" 3 | version = "0.1.0" 4 | dependencies = [ 5 | "nom 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 6 | ] 7 | 8 | [[package]] 9 | name = "libc" 10 | version = "0.2.42" 11 | source = "registry+https://github.com/rust-lang/crates.io-index" 12 | 13 | [[package]] 14 | name = "memchr" 15 | version = "2.0.1" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | dependencies = [ 18 | "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", 19 | ] 20 | 21 | [[package]] 22 | name = "nom" 23 | version = "4.0.0" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | dependencies = [ 26 | "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 27 | ] 28 | 29 | [metadata] 30 | "checksum libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)" = "b685088df2b950fccadf07a7187c8ef846a959c142338a48f9dc0b94517eb5f1" 31 | "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" 32 | "checksum nom 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "898696750eb5c3ce5eb5afbfbe46e7f7c4e1936e19d3e97be4b7937da7b6d114" 33 | -------------------------------------------------------------------------------- /cldr_pluralrules_parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cldr_pluralrules_parser" 3 | version = "2.0.0" 4 | edition = "2018" 5 | authors = ["Kekoa Riggin ", "Zibi Braniecki "] 6 | description = "A parser for CLDR plural rules." 7 | license = "Apache-2.0/MIT" 8 | repository = "https://github.com/unclenachoduh/pluralrules" 9 | readme = "README.md" 10 | keywords = ["localization", "l10n", "i18n", "intl", "internationalization"] 11 | categories = ["localization", "internationalization"] 12 | include = [ 13 | "src/**/*", 14 | "benches/*.rs", 15 | "Cargo.toml", 16 | "README.md" 17 | ] 18 | 19 | [badges] 20 | travis-ci = { repository = "zbraniecki/pluralrules", branch = "master" } 21 | coveralls = { repository = "zbraniecki/pluralrules", branch = "master", service = "github" } 22 | 23 | maintenance = { status = "actively-developed" } 24 | 25 | [dependencies] 26 | nom = "5.0" 27 | 28 | [dev-dependencies] 29 | criterion = "0.3" 30 | 31 | [[bench]] 32 | name = "parser" 33 | harness = false 34 | -------------------------------------------------------------------------------- /cldr_pluralrules_parser/README.md: -------------------------------------------------------------------------------- 1 | # CLDR PluralRules Parser 2 | 3 | `cldr_pluralrules_parser` is a parser library for [CLDR][] [PluralRules][]. 4 | 5 | [![crates.io](https://img.shields.io/crates/v/cldr_pluralrules_parser.svg)](https://crates.io/crates/cldr_pluralrules_parser) 6 | [![Build Status](https://travis-ci.org/zbraniecki/pluralrules.svg?branch=master)](https://travis-ci.org/zbraniecki/pluralrules) 7 | [![Coverage Status](https://coveralls.io/repos/github/zbraniecki/pluralrules/badge.svg?branch=master)](https://coveralls.io/github/zbraniecki/pluralrules?branch=master) 8 | 9 | The library closely follows [LDML Language Plural Rules Syntax][] and is intended to be 10 | used at build time or runtime to construct operations necessary for calculating 11 | corret plural rules categories. 12 | 13 | Status 14 | ------ 15 | 16 | The parser is functionally complete and follows the current version of the syntax. 17 | 18 | It is lenient and open to contributions in the area of conformance, testing, and 19 | rejecting invalid input. 20 | 21 | Local Development 22 | ----------------- 23 | 24 | cargo build 25 | cargo test 26 | 27 | When submitting a PR please use `cargo fmt`. 28 | 29 | [CLDR]: https://cldr.unicode.org/ 30 | [PluralRules]: https://cldr.unicode.org/index/cldr-spec/plural-rules 31 | [LDML Language Plural Rules Syntax]: https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules 32 | -------------------------------------------------------------------------------- /cldr_pluralrules_parser/benches/parser.rs: -------------------------------------------------------------------------------- 1 | use criterion::black_box; 2 | use criterion::criterion_group; 3 | use criterion::criterion_main; 4 | use criterion::Criterion; 5 | 6 | use cldr_pluralrules_parser::{parse_plural_condition, parse_plural_rule}; 7 | 8 | const STRINGS: &[&str] = &[ 9 | "n % 10 = 0 or n % 10 = 5..9 or n % 100 = 11..14 @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …", 10 | "n % 10 = 2..4 and n % 100 != 12..14 @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, … @decimal 2.0, 3.0, 4.0, 22.0, 23.0, 24.0, 32.0, 33.0, 102.0, 1002.0, …", 11 | "n % 10 = 1 and n % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 1.0, 21.0, 31.0, 41.0, 51.0, 61.0, 71.0, 81.0, 101.0, 1001.0, …", 12 | "n % 100 = 11..99 @integer 11~26, 111, 1011, … @decimal 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 111.0, 1011.0, …", 13 | ]; 14 | 15 | fn cldr_parse_plural_rule(c: &mut Criterion) { 16 | c.bench_function("parse_plural_condition", |b| { 17 | b.iter(|| { 18 | for s in STRINGS { 19 | let _ = parse_plural_condition(black_box(s)).expect("Parsing succeeded"); 20 | } 21 | }) 22 | }); 23 | 24 | c.bench_function("parse_plural_rule", |b| { 25 | b.iter(|| { 26 | for s in STRINGS { 27 | let _ = parse_plural_rule(black_box(s)).expect("Parsing succeeded"); 28 | } 29 | }) 30 | }); 31 | } 32 | 33 | criterion_group!(benches, cldr_parse_plural_rule,); 34 | criterion_main!(benches); 35 | -------------------------------------------------------------------------------- /cldr_pluralrules_parser/src/ast.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone, PartialEq)] 2 | pub struct Rule { 3 | pub condition: Condition, 4 | pub samples: Option, 5 | } 6 | 7 | #[derive(Debug, Clone, PartialEq)] 8 | pub struct Samples { 9 | pub integer: Option, 10 | pub decimal: Option, 11 | } 12 | 13 | #[derive(Debug, Clone, PartialEq)] 14 | pub struct SampleList { 15 | pub sample_ranges: Vec, 16 | pub ellipsis: bool, 17 | } 18 | 19 | #[derive(Debug, Clone, PartialEq)] 20 | pub struct SampleRange { 21 | pub lower_val: DecimalValue, 22 | pub upper_val: Option, 23 | } 24 | 25 | #[derive(Debug, Clone, PartialEq)] 26 | pub struct DecimalValue { 27 | pub integer: Value, 28 | pub decimal: Option, 29 | } 30 | 31 | /// A complete (and the only complete) AST representation of a plural rule. Comprises a vector of AndConditions. 32 | /// 33 | /// # Examples 34 | /// 35 | /// All AST nodes can be built explicitly, as seen in the example. However, due to its complexity, it is preferred to build the AST using the parse_plural_rule function. 36 | /// 37 | /// ```text 38 | /// "i = 5" 39 | /// ``` 40 | /// 41 | /// Can be represented by the AST: 42 | /// 43 | /// ``` 44 | /// use cldr_pluralrules_parser::ast::*; 45 | /// 46 | /// Condition(vec![AndCondition(vec![Relation { 47 | /// expression: Expression { 48 | /// operand: Operand::I, 49 | /// modulus: None, 50 | /// }, 51 | /// operator: Operator::EQ, 52 | /// range_list: RangeList(vec![RangeListItem::Value(Value(5))]), 53 | /// }])]); 54 | /// ``` 55 | /// 56 | /// Because they care complete representations, hand-written Conditions can be verified with the assert macro. No other AST nodes can be verified. 57 | /// 58 | /// ``` 59 | /// use cldr_pluralrules_parser::ast::*; 60 | /// use cldr_pluralrules_parser::parse_plural_rule; 61 | /// 62 | /// let condition = Condition(vec![ 63 | /// AndCondition(vec![Relation { 64 | /// expression: Expression { 65 | /// operand: Operand::I, 66 | /// modulus: None, 67 | /// }, 68 | /// operator: Operator::Is, 69 | /// range_list: RangeList(vec![RangeListItem::Value(Value(5))]), 70 | /// }]), 71 | /// AndCondition(vec![Relation { 72 | /// expression: Expression { 73 | /// operand: Operand::V, 74 | /// modulus: None, 75 | /// }, 76 | /// operator: Operator::Within, 77 | /// range_list: RangeList(vec![RangeListItem::Value(Value(2))]), 78 | /// }]), 79 | /// ]); 80 | /// 81 | /// assert_eq!( 82 | /// condition, 83 | /// parse_plural_rule("i is 5 or v within 2") 84 | /// .expect("Parsing succeeded") 85 | /// .condition 86 | /// ) 87 | /// ``` 88 | #[derive(Debug, Clone, PartialEq)] 89 | pub struct Condition(pub Vec); 90 | 91 | /// An incomplete AST representation of a plural rule. Comprises a vector of Relations. 92 | /// 93 | /// # Examples 94 | /// 95 | /// All AST nodes can be built explicitly, as seen in the example. However, due to its complexity, it is preferred to build the AST using the parse_plural_rule function. 96 | /// 97 | /// ```text 98 | /// "i = 3 and v = 0" 99 | /// ``` 100 | /// 101 | /// Can be represented by the AST: 102 | /// 103 | /// ``` 104 | /// use cldr_pluralrules_parser::ast::*; 105 | /// 106 | /// AndCondition(vec![ 107 | /// Relation { 108 | /// expression: Expression { 109 | /// operand: Operand::I, 110 | /// modulus: None, 111 | /// }, 112 | /// operator: Operator::In, 113 | /// range_list: RangeList(vec![RangeListItem::Value(Value(5))]), 114 | /// }, 115 | /// Relation { 116 | /// expression: Expression { 117 | /// operand: Operand::V, 118 | /// modulus: None, 119 | /// }, 120 | /// operator: Operator::NotIn, 121 | /// range_list: RangeList(vec![RangeListItem::Value(Value(2))]), 122 | /// }, 123 | /// ]); 124 | /// 125 | /// ``` 126 | #[derive(Debug, Clone, PartialEq)] 127 | pub struct AndCondition(pub Vec); 128 | 129 | /// An incomplete AST representation of a plural rule. Comprises an Expression, an Operator, and a RangeList. 130 | /// 131 | /// # Examples 132 | /// 133 | /// All AST nodes can be built explicitly, as seen in the example. However, due to its complexity, it is preferred to build the AST using the parse_plural_rule function. 134 | /// 135 | /// ```text 136 | /// "i = 3" 137 | /// ``` 138 | /// 139 | /// Can be represented by the AST: 140 | /// 141 | /// ``` 142 | /// use cldr_pluralrules_parser::ast::*; 143 | /// 144 | /// Relation { 145 | /// expression: Expression { 146 | /// operand: Operand::I, 147 | /// modulus: None, 148 | /// }, 149 | /// operator: Operator::Is, 150 | /// range_list: RangeList(vec![RangeListItem::Value(Value(3))]), 151 | /// }; 152 | /// 153 | /// ``` 154 | #[derive(Debug, Clone, PartialEq)] 155 | pub struct Relation { 156 | pub expression: Expression, 157 | pub operator: Operator, 158 | pub range_list: RangeList, 159 | } 160 | 161 | /// An enum of Relation operators for plural rules. 162 | /// 163 | /// Each Operator enumeration belongs to the corresponding symbolic operators: 164 | /// 165 | /// | Enum Operator | Symbolic Operator | 166 | /// | - | - | 167 | /// | In | "in" | 168 | /// | NotIn | "not in" | 169 | /// | Within | "within" | 170 | /// | NotWithin | "not within" | 171 | /// | Is | "is" | 172 | /// | IsNot | "is not" | 173 | /// | EQ | "=" | 174 | /// | NotEq | "!=" | 175 | /// 176 | #[derive(Debug, Clone, PartialEq)] 177 | pub enum Operator { 178 | In, 179 | NotIn, 180 | Within, 181 | NotWithin, 182 | Is, 183 | IsNot, 184 | EQ, 185 | NotEQ, 186 | } 187 | 188 | /// An incomplete AST representation of a plural rule. Comprises an Operand and an optional Modulo. 189 | /// 190 | /// # Examples 191 | /// 192 | /// All AST nodes can be built explicitly, as seen in the example. However, due to its complexity, it is preferred to build the AST using the parse_plural_rule function. 193 | /// 194 | /// ```text 195 | /// "i % 100" 196 | /// ``` 197 | /// 198 | /// Can be represented by the AST: 199 | /// 200 | /// ``` 201 | /// use cldr_pluralrules_parser::ast::*; 202 | /// 203 | /// Expression { 204 | /// operand: Operand::I, 205 | /// modulus: Some(Modulo(Value(100))), 206 | /// }; 207 | /// 208 | /// ``` 209 | #[derive(Debug, Clone, PartialEq)] 210 | pub struct Expression { 211 | pub operand: Operand, 212 | pub modulus: Option, 213 | } 214 | 215 | /// An incomplete AST representation of a plural rule. Comprises a Value but is later expressed as `% usize`. 216 | /// 217 | /// # Examples 218 | /// 219 | /// All AST nodes can be built explicitly, as seen in the example. However, due to its complexity, it is preferred to build the AST using the parse_plural_rule function. 220 | /// 221 | /// ```text 222 | /// "% 100" 223 | /// ``` 224 | /// 225 | /// Will be used to represent the AST: 226 | /// 227 | /// ``` 228 | /// use cldr_pluralrules_parser::ast::*; 229 | /// 230 | /// Modulo(Value(100)); 231 | /// ``` 232 | #[derive(Debug, Clone, PartialEq)] 233 | pub struct Modulo(pub Value); 234 | 235 | /// An incomplete AST representation of a plural rule. Comprises a char. 236 | /// 237 | /// # Examples 238 | /// 239 | /// All AST nodes can be built explicitly, as seen in the example. However, due to its complexity, it is preferred to build the AST using the parse_plural_rule function. 240 | /// 241 | /// ```text 242 | /// "i" 243 | /// ``` 244 | /// 245 | /// Can be represented by the AST: 246 | /// 247 | /// ``` 248 | /// use cldr_pluralrules_parser::ast::Operand; 249 | /// 250 | /// Operand::I; 251 | /// ``` 252 | #[derive(Debug, Clone, PartialEq)] 253 | pub enum Operand { 254 | N, // Absolute value of input 255 | I, // Integer value of input 256 | V, // Number of visible fraction digits with trailing zeros 257 | W, // Number of visible fraction digits without trailing zeros 258 | F, // Visible fraction digits with trailing zeros 259 | T, // Visible fraction digits without trailing zeros 260 | } 261 | 262 | /// An incomplete AST representation of a plural rule. Comprises a vector of RangeListItems. 263 | /// 264 | /// # Examples 265 | /// 266 | /// All AST nodes can be built explicitly, as seen in the example. However, due to its complexity, it is preferred to build the AST using the parse_plural_rule function. 267 | /// 268 | /// ```text 269 | /// "5, 7, 9" 270 | /// ``` 271 | /// 272 | /// Can be represented by the AST: 273 | /// 274 | /// ``` 275 | /// use cldr_pluralrules_parser::ast::*; 276 | /// 277 | /// RangeList(vec![ 278 | /// RangeListItem::Value(Value(5)), 279 | /// RangeListItem::Value(Value(7)), 280 | /// RangeListItem::Value(Value(9)), 281 | /// ]); 282 | /// ``` 283 | #[derive(Debug, Clone, PartialEq)] 284 | pub struct RangeList(pub Vec); 285 | 286 | /// An enum of items that appear in a RangeList: Range or a Value. 287 | /// 288 | /// See Range and Value for additional details. 289 | /// 290 | #[derive(Debug, Clone, PartialEq)] 291 | pub enum RangeListItem { 292 | Range(Range), 293 | Value(Value), 294 | } 295 | 296 | /// An incomplete AST representation of a plural rule. Comprises two Values: an inclusive lower and upper limit. 297 | /// 298 | /// # Examples 299 | /// 300 | /// All AST nodes can be built explicitly, as seen in the example. However, due to its complexity, it is preferred to build the AST using the parse_plural_rule function. 301 | /// 302 | /// ```text 303 | /// "11..15" 304 | /// ``` 305 | /// 306 | /// Can be represented by the AST: 307 | /// 308 | /// ``` 309 | /// use cldr_pluralrules_parser::ast::*; 310 | /// 311 | /// RangeListItem::Range(Range { 312 | /// lower_val: Value(11), 313 | /// upper_val: Value(15), 314 | /// }); 315 | /// ``` 316 | #[derive(Debug, Clone, PartialEq)] 317 | pub struct Range { 318 | pub lower_val: Value, 319 | pub upper_val: Value, 320 | } 321 | 322 | /// An incomplete AST representation of a plural rule, representing one integer. 323 | /// 324 | /// # Examples 325 | /// 326 | /// All AST nodes can be built explicitly, as seen in the example. However, due to its complexity, it is preferred to build the AST using the parse_plural_rule function. 327 | /// 328 | /// ```text 329 | /// "99" 330 | /// ``` 331 | /// 332 | /// Can be represented by the AST: 333 | /// 334 | /// ``` 335 | /// use cldr_pluralrules_parser::ast::*; 336 | /// 337 | /// RangeListItem::Value(Value(99)); 338 | /// ``` 339 | #[derive(Debug, Clone, PartialEq)] 340 | pub struct Value(pub usize); 341 | -------------------------------------------------------------------------------- /cldr_pluralrules_parser/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A crate for parsing CLDR plural rules. 2 | //! 3 | //! This crate parses plural rules and returns an AST representation of the rule. Plural rules must be written according to the specifications outlined at [Unicode's website](https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules). 4 | //! 5 | //! Plural rules, compatible with this crate, can be found at [this GitHub repository](https://github.com/unicode-cldr/cldr-core/blob/master/supplemental/plurals.json). 6 | //! 7 | //! # Examples 8 | //! 9 | //! ``` 10 | //! use cldr_pluralrules_parser::parse_plural_rule; 11 | //! use cldr_pluralrules_parser::ast::*; 12 | //! 13 | //! let condition = Condition(vec![ 14 | //! AndCondition(vec![Relation { 15 | //! expression: Expression { 16 | //! operand: Operand::I, 17 | //! modulus: None, 18 | //! }, 19 | //! operator: Operator::Is, 20 | //! range_list: RangeList(vec![RangeListItem::Value(Value(5))]), 21 | //! }]), 22 | //! AndCondition(vec![Relation { 23 | //! expression: Expression { 24 | //! operand: Operand::V, 25 | //! modulus: None, 26 | //! }, 27 | //! operator: Operator::Within, 28 | //! range_list: RangeList(vec![RangeListItem::Value(Value(2))]), 29 | //! }]), 30 | //! ]); 31 | //! 32 | //! assert_eq!( 33 | //! condition, 34 | //! parse_plural_rule("i is 5 or v within 2") 35 | //! .expect("Parsing succeeded") 36 | //! .condition 37 | //! ) 38 | //! ``` 39 | 40 | /// A public AST module for plural rule representations. 41 | pub mod ast; 42 | /// A private parsing module for plural rules. 43 | mod parser; 44 | 45 | /// Given a string reference of a plural rule, will return the AST representation of that rule. 46 | /// 47 | /// # Examples 48 | /// 49 | /// ``` 50 | /// use cldr_pluralrules_parser::parse_plural_rule; 51 | /// use cldr_pluralrules_parser::ast::*; 52 | /// 53 | /// let condition = Condition(vec![ 54 | /// AndCondition(vec![Relation { 55 | /// expression: Expression { 56 | /// operand: Operand::I, 57 | /// modulus: None, 58 | /// }, 59 | /// operator: Operator::Is, 60 | /// range_list: RangeList(vec![RangeListItem::Value(Value(5))]), 61 | /// }]), 62 | /// AndCondition(vec![Relation { 63 | /// expression: Expression { 64 | /// operand: Operand::V, 65 | /// modulus: None, 66 | /// }, 67 | /// operator: Operator::Within, 68 | /// range_list: RangeList(vec![RangeListItem::Value(Value(2))]), 69 | /// }]), 70 | /// ]); 71 | /// 72 | /// assert_eq!( 73 | /// condition, 74 | /// parse_plural_rule("i is 5 or v within 2") 75 | /// .expect("Parsing succeeded") 76 | /// .condition 77 | /// ) 78 | /// ``` 79 | pub fn parse_plural_rule>(source: S) -> Result { 80 | match parser::parse_rule(source.as_ref()) { 81 | Ok(("", rule)) => Ok(rule), 82 | //Ok((_, rule)) => Ok(rule), 83 | Ok((left, _)) => Err(format!("Left string: {}", left)), 84 | _ => Err("Parser failed".to_string()), 85 | } 86 | } 87 | 88 | pub fn parse_plural_condition>(source: S) -> Result { 89 | match parser::parse_condition(source.as_ref()) { 90 | Ok((_, rule)) => Ok(rule), 91 | _ => Err("Parser failed".to_string()), 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /cldr_pluralrules_parser/src/parser.rs: -------------------------------------------------------------------------------- 1 | use super::ast::*; 2 | use nom::{ 3 | branch::alt, 4 | //error::context, 5 | bytes::complete::tag, 6 | character::complete::{digit1, one_of, space0, space1}, 7 | combinator::{map, map_res, opt}, 8 | multi::{separated_list, separated_nonempty_list}, 9 | sequence::{preceded, separated_pair, tuple}, 10 | IResult, 11 | }; 12 | 13 | fn value(i: &str) -> IResult<&str, Value> { 14 | map_res(digit1, |s: &str| s.parse::().map(Value))(i) 15 | } 16 | 17 | fn range(i: &str) -> IResult<&str, Range> { 18 | map( 19 | separated_pair(value, tag(".."), value), 20 | |(lower_val, upper_val)| Range { 21 | lower_val, 22 | upper_val, 23 | }, 24 | )(i) 25 | } 26 | 27 | fn range_list_item(i: &str) -> IResult<&str, RangeListItem> { 28 | alt(( 29 | map(range, RangeListItem::Range), 30 | map(value, RangeListItem::Value), 31 | ))(i) 32 | } 33 | 34 | fn range_list(i: &str) -> IResult<&str, RangeList> { 35 | map( 36 | separated_list(tuple((space0, tag(","), space0)), range_list_item), 37 | RangeList, 38 | )(i) 39 | } 40 | 41 | fn operand(i: &str) -> IResult<&str, Operand> { 42 | map(one_of("nivwft"), |c| match c { 43 | 'n' => Operand::N, 44 | 'i' => Operand::I, 45 | 'v' => Operand::V, 46 | 'w' => Operand::W, 47 | 'f' => Operand::F, 48 | 't' => Operand::T, 49 | _ => unreachable!(), 50 | })(i) 51 | } 52 | 53 | fn mod_expression(i: &str) -> IResult<&str, Option> { 54 | opt(map( 55 | preceded(tuple((space0, alt((tag("mod"), tag("%"))), space1)), value), 56 | Modulo, 57 | ))(i) 58 | } 59 | 60 | fn expression(i: &str) -> IResult<&str, Expression> { 61 | map(tuple((operand, mod_expression)), |(operand, modulus)| { 62 | Expression { operand, modulus } 63 | })(i) 64 | } 65 | 66 | fn relation_operator(i: &str) -> IResult<&str, Operator> { 67 | alt(( 68 | map(tag("="), |_| Operator::EQ), 69 | map(tag("!="), |_| Operator::NotEQ), 70 | map(tuple((tag("is"), space1, opt(tag("not")))), |(_, _, n)| { 71 | if n.is_some() { 72 | Operator::IsNot 73 | } else { 74 | Operator::Is 75 | } 76 | }), 77 | map(tag("in"), |_| Operator::In), 78 | map( 79 | tuple(( 80 | tag("not"), 81 | space1, 82 | alt(( 83 | map(tag("in"), |_| Operator::NotIn), 84 | map(tag("within"), |_| Operator::NotWithin), 85 | )), 86 | )), 87 | |(_, _, v)| v, 88 | ), 89 | map(tag("within"), |_| Operator::Within), 90 | ))(i) 91 | } 92 | 93 | fn relation(i: &str) -> IResult<&str, Relation> { 94 | map( 95 | tuple((expression, space0, relation_operator, space0, range_list)), 96 | |(expression, _, operator, _, range_list)| Relation { 97 | expression, 98 | operator, 99 | range_list, 100 | }, 101 | )(i) 102 | } 103 | 104 | fn and_condition(i: &str) -> IResult<&str, AndCondition> { 105 | map( 106 | separated_nonempty_list(tuple((space1, tag("and"), space1)), relation), 107 | AndCondition, 108 | )(i) 109 | } 110 | 111 | fn decimal_value(i: &str) -> IResult<&str, DecimalValue> { 112 | map( 113 | tuple((value, opt(preceded(tag("."), value)))), 114 | |(integer, decimal)| DecimalValue { integer, decimal }, 115 | )(i) 116 | } 117 | 118 | fn sample_range(i: &str) -> IResult<&str, SampleRange> { 119 | map( 120 | tuple(( 121 | decimal_value, 122 | opt(preceded(tuple((space0, tag("~"), space0)), decimal_value)), 123 | )), 124 | |(lower_val, upper_val)| SampleRange { 125 | lower_val, 126 | upper_val, 127 | }, 128 | )(i) 129 | } 130 | 131 | fn sample_list(i: &str) -> IResult<&str, SampleList> { 132 | map( 133 | tuple(( 134 | separated_nonempty_list(tuple((space0, tag(","), space0)), sample_range), 135 | opt(preceded( 136 | tuple((space0, tag(","), space0)), 137 | alt((tag("..."), tag("…"))), 138 | )), 139 | )), 140 | |(l, ellipsis)| SampleList { 141 | sample_ranges: l, 142 | ellipsis: ellipsis.is_some(), 143 | }, 144 | )(i) 145 | } 146 | 147 | fn samples(i: &str) -> IResult<&str, Option> { 148 | map( 149 | tuple(( 150 | opt(preceded( 151 | tuple((space1, tag("@integer"), space1)), 152 | sample_list, 153 | )), 154 | opt(preceded( 155 | tuple((space1, tag("@decimal"), space1)), 156 | sample_list, 157 | )), 158 | )), 159 | |(integer, decimal)| { 160 | if integer.is_some() || decimal.is_some() { 161 | Some(Samples { integer, decimal }) 162 | } else { 163 | None 164 | } 165 | }, 166 | )(i) 167 | } 168 | 169 | pub fn parse_rule(i: &str) -> IResult<&str, Rule> { 170 | map(tuple((parse_condition, samples)), |(condition, samples)| { 171 | Rule { condition, samples } 172 | })(i) 173 | } 174 | 175 | pub fn parse_condition(i: &str) -> IResult<&str, Condition> { 176 | // We need to handle empty input and/or input that is empty until sample. 177 | if i.trim().is_empty() { 178 | return IResult::Ok(("", Condition(vec![]))); 179 | } 180 | 181 | if i.trim().starts_with("@") { 182 | return IResult::Ok(("", Condition(vec![]))); 183 | } 184 | map( 185 | separated_nonempty_list(tuple((space1, tag("or"), space1)), and_condition), 186 | Condition, 187 | )(i) 188 | } 189 | -------------------------------------------------------------------------------- /cldr_pluralrules_parser/tests/conditions.rs: -------------------------------------------------------------------------------- 1 | use cldr_pluralrules_parser::ast::*; 2 | use cldr_pluralrules_parser::*; 3 | 4 | #[test] 5 | fn simple_empty() { 6 | let test = ""; 7 | 8 | assert_eq!( 9 | Condition(vec![]), 10 | parse_plural_rule(test) 11 | .expect("Parsing succeeded") 12 | .condition 13 | ); 14 | 15 | let test = " "; 16 | 17 | assert_eq!( 18 | Condition(vec![]), 19 | parse_plural_rule(test) 20 | .expect("Parsing succeeded") 21 | .condition 22 | ); 23 | 24 | let test = "@integer 0"; 25 | 26 | assert_eq!( 27 | Condition(vec![]), 28 | parse_plural_rule(test) 29 | .expect("Parsing succeeded") 30 | .condition 31 | ); 32 | 33 | let test = " @integer 0"; 34 | 35 | assert_eq!( 36 | Condition(vec![]), 37 | parse_plural_rule(test) 38 | .expect("Parsing succeeded") 39 | .condition 40 | ); 41 | } 42 | 43 | #[test] 44 | fn simple_expression() { 45 | let test = "i = 5"; 46 | 47 | assert_eq!( 48 | Condition(vec![AndCondition(vec![Relation { 49 | expression: Expression { 50 | operand: Operand::I, 51 | modulus: None, 52 | }, 53 | operator: Operator::EQ, 54 | range_list: RangeList(vec![RangeListItem::Value(Value(5))]), 55 | }])]), 56 | parse_plural_rule(test) 57 | .expect("Parsing succeeded") 58 | .condition 59 | ); 60 | } 61 | 62 | #[test] 63 | fn multi_value() { 64 | let test = "i = 5, 7, 9"; 65 | 66 | assert_eq!( 67 | Condition(vec![AndCondition(vec![Relation { 68 | expression: Expression { 69 | operand: Operand::I, 70 | modulus: None, 71 | }, 72 | operator: Operator::EQ, 73 | range_list: RangeList(vec![ 74 | RangeListItem::Value(Value(5)), 75 | RangeListItem::Value(Value(7)), 76 | RangeListItem::Value(Value(9)), 77 | ]), 78 | }])]), 79 | parse_plural_rule(test) 80 | .expect("Parsing succeeded") 81 | .condition 82 | ); 83 | } 84 | 85 | #[test] 86 | fn multi_range() { 87 | let test = "i in 5..9, 11..15"; 88 | 89 | assert_eq!( 90 | Condition(vec![AndCondition(vec![Relation { 91 | expression: Expression { 92 | operand: Operand::I, 93 | modulus: None, 94 | }, 95 | operator: Operator::In, 96 | range_list: RangeList(vec![ 97 | RangeListItem::Range(Range { 98 | lower_val: Value(5), 99 | upper_val: Value(9), 100 | }), 101 | RangeListItem::Range(Range { 102 | lower_val: Value(11), 103 | upper_val: Value(15), 104 | }), 105 | ]), 106 | }])]), 107 | parse_plural_rule(test) 108 | .expect("Parsing succeeded") 109 | .condition 110 | ); 111 | } 112 | 113 | #[test] 114 | fn and_condition() { 115 | let test = "i in 5 and v not in 2"; 116 | 117 | assert_eq!( 118 | Condition(vec![AndCondition(vec![ 119 | Relation { 120 | expression: Expression { 121 | operand: Operand::I, 122 | modulus: None, 123 | }, 124 | operator: Operator::In, 125 | range_list: RangeList(vec![RangeListItem::Value(Value(5))]), 126 | }, 127 | Relation { 128 | expression: Expression { 129 | operand: Operand::V, 130 | modulus: None, 131 | }, 132 | operator: Operator::NotIn, 133 | range_list: RangeList(vec![RangeListItem::Value(Value(2))]), 134 | }, 135 | ])]), 136 | parse_plural_rule(test) 137 | .expect("Parsing succeeded") 138 | .condition 139 | ); 140 | } 141 | 142 | #[test] 143 | fn or_condition() { 144 | let test = "i is 5 or v within 2"; 145 | 146 | assert_eq!( 147 | Condition(vec![ 148 | AndCondition(vec![Relation { 149 | expression: Expression { 150 | operand: Operand::I, 151 | modulus: None, 152 | }, 153 | operator: Operator::Is, 154 | range_list: RangeList(vec![RangeListItem::Value(Value(5))]), 155 | }]), 156 | AndCondition(vec![Relation { 157 | expression: Expression { 158 | operand: Operand::V, 159 | modulus: None, 160 | }, 161 | operator: Operator::Within, 162 | range_list: RangeList(vec![RangeListItem::Value(Value(2))]), 163 | }]), 164 | ]), 165 | parse_plural_rule(test) 166 | .expect("Parsing succeeded") 167 | .condition 168 | ); 169 | } 170 | 171 | #[test] 172 | fn ars_many_rule() { 173 | let test = "n % 100 = 11..99 @integer 11~26, 111, 1011, … @decimal 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 111.0, 1011.0, …"; 174 | 175 | assert_eq!( 176 | Condition(vec![AndCondition(vec![Relation { 177 | expression: Expression { 178 | operand: Operand::N, 179 | modulus: Some(Modulo(Value(100))), 180 | }, 181 | operator: Operator::EQ, 182 | range_list: RangeList(vec![RangeListItem::Range(Range { 183 | lower_val: Value(11), 184 | upper_val: Value(99), 185 | })]), 186 | }])]), 187 | parse_plural_rule(test) 188 | .expect("Parsing succeeded") 189 | .condition 190 | ); 191 | } 192 | 193 | #[test] 194 | fn be_one_rule() { 195 | let test = "n % 10 = 1 and n % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 1.0, 21.0, 31.0, 41.0, 51.0, 61.0, 71.0, 81.0, 101.0, 1001.0, …"; 196 | assert_eq!( 197 | Condition(vec![AndCondition(vec![ 198 | Relation { 199 | expression: Expression { 200 | operand: Operand::N, 201 | modulus: Some(Modulo(Value(10))), 202 | }, 203 | operator: Operator::EQ, 204 | range_list: RangeList(vec![RangeListItem::Value(Value(1))]), 205 | }, 206 | Relation { 207 | expression: Expression { 208 | operand: Operand::N, 209 | modulus: Some(Modulo(Value(100))), 210 | }, 211 | operator: Operator::NotEQ, 212 | range_list: RangeList(vec![RangeListItem::Value(Value(11))]), 213 | }, 214 | ])]), 215 | parse_plural_rule(test) 216 | .expect("Parsing succeeded") 217 | .condition 218 | ); 219 | } 220 | 221 | #[test] 222 | fn be_few_rule() { 223 | let test = "n % 10 = 2..4 and n % 100 != 12..14 @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, … @decimal 2.0, 3.0, 4.0, 22.0, 23.0, 24.0, 32.0, 33.0, 102.0, 1002.0, …"; 224 | assert_eq!( 225 | Condition(vec![AndCondition(vec![ 226 | Relation { 227 | expression: Expression { 228 | operand: Operand::N, 229 | modulus: Some(Modulo(Value(10))), 230 | }, 231 | operator: Operator::EQ, 232 | range_list: RangeList(vec![RangeListItem::Range(Range { 233 | lower_val: Value(2), 234 | upper_val: Value(4), 235 | })]), 236 | }, 237 | Relation { 238 | expression: Expression { 239 | operand: Operand::N, 240 | modulus: Some(Modulo(Value(100))), 241 | }, 242 | operator: Operator::NotEQ, 243 | range_list: RangeList(vec![RangeListItem::Range(Range { 244 | lower_val: Value(12), 245 | upper_val: Value(14), 246 | })]), 247 | }, 248 | ])]), 249 | parse_plural_rule(test) 250 | .expect("Parsing succeeded") 251 | .condition 252 | ); 253 | } 254 | 255 | #[test] 256 | fn be_many_rule() { 257 | let test = "n % 10 = 0 or n % 10 = 5..9 or n % 100 = 11..14 @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"; 258 | assert_eq!( 259 | Condition(vec![ 260 | AndCondition(vec![Relation { 261 | expression: Expression { 262 | operand: Operand::N, 263 | modulus: Some(Modulo(Value(10))), 264 | }, 265 | operator: Operator::EQ, 266 | range_list: RangeList(vec![RangeListItem::Value(Value(0))]), 267 | }]), 268 | AndCondition(vec![Relation { 269 | expression: Expression { 270 | operand: Operand::N, 271 | modulus: Some(Modulo(Value(10))), 272 | }, 273 | operator: Operator::EQ, 274 | range_list: RangeList(vec![RangeListItem::Range(Range { 275 | lower_val: Value(5), 276 | upper_val: Value(9), 277 | })]), 278 | }]), 279 | AndCondition(vec![Relation { 280 | expression: Expression { 281 | operand: Operand::N, 282 | modulus: Some(Modulo(Value(100))), 283 | }, 284 | operator: Operator::EQ, 285 | range_list: RangeList(vec![RangeListItem::Range(Range { 286 | lower_val: Value(11), 287 | upper_val: Value(14), 288 | })]), 289 | }]), 290 | ]), 291 | parse_plural_rule(test) 292 | .expect("Parsing succeeded") 293 | .condition 294 | ); 295 | } 296 | -------------------------------------------------------------------------------- /intl_pluralrules/.cargo/config: -------------------------------------------------------------------------------- 1 | [alias] 2 | regenerate-data = "run -p make_pluralrules -- -i ./cldr_data/cldr_pluralrules_cardinals.json ./cldr_data/cldr_pluralrules_ordinals.json -o ./src/rules.rs" 3 | -------------------------------------------------------------------------------- /intl_pluralrules/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Unreleased 4 | 5 | - … 6 | 7 | ## intl_pluralrules 7.0.1 (January 5, 2021) 8 | - Fix for tarpaulin. (#36) 9 | 10 | ## intl_pluralrules 7.0.0 (May 6, 2020) 11 | 12 | - Update `unic-langid` to 0.9. 13 | - Make sure not to panic when f64 numbers are passed on 32bit systems. 14 | 15 | ## intl_pluralrules 6.0.0 (January 28, 2020) 16 | 17 | - Switch to use `TryFrom`/`TryInto` for `PluralOperands`. 18 | - Update `unic-langid` to 0.8. 19 | - Optimize `PluralOperands` struct memory layout. 20 | 21 | ## intl_pluralrules 5.0.2 (December 19, 2019) 22 | 23 | - Make `PluralRuleType` derive `Hash` and `Eq`. 24 | 25 | ## intl_pluralrules 5.0.1 (November 20, 2019) 26 | 27 | - The `rules.rs` grew too big, so use macro to shrink it. 28 | 29 | ## intl_pluralrules 5.0.0 (November 13, 2019) 30 | 31 | - Renamed `IntlPluralRules` to `PluralRules` in line with 32 | other internationalization naming conventions. 33 | - Use updated generated tables which operate on sorted 34 | vectors of `(LanguageIdentifier, PluralRule)` tuples. 35 | - Improved performance of construction of `PluralRules` by 66%. 36 | - Removed dependency on `phf`. 37 | 38 | ## intl_pluralrules 4.0.1 (October 15, 2019) 39 | 40 | - Update CLDR to 36. 41 | 42 | ## intl_pluralrules 4.0.0 (October 3, 2019) 43 | 44 | - Upgrade to unic-langid 0.6. 45 | 46 | ## intl_pluralrules 3.0.0 (September 10, 2019) 47 | 48 | - Upgrade to unic-langid 0.5. 49 | 50 | ## intl_pluralrules 2.0.0 (July 26, 2019) 51 | 52 | - Switch to unic_langid for Language Identifiers. 53 | 54 | ## intl_pluralrules 1.0.3 (March 29, 2019) 55 | 56 | - Update CLDR to 35 57 | 58 | ## intl_pluralrules 1.0.2 (January 7, 2019) 59 | 60 | - Update to Rust 2018. 61 | 62 | ## intl_pluralrules 1.0.1 (October 26, 2018) 63 | 64 | - Update CLDR to 34 65 | - Update rules to pass enums by reference 66 | 67 | ## intl_pluralrules 1.0.0 (August 15, 2018) 68 | 69 | - Add dedicated impls for PluralOperands::from 70 | - New trait extends impls to custom types 71 | - Other minor optimizations 72 | 73 | ## intl_pluralrules 0.9.1 (August 6, 2018) 74 | 75 | - Updates to docs for PluralRuleType. 76 | - Move PluralRuleType from rules.rs to lib.rs. 77 | - Fix lib.rs doc. 78 | 79 | ## intl_pluralrules 0.9.0 (August 3, 2018) 80 | 81 | - Updates to docs. 82 | 83 | ## intl_pluralrules 0.8.2 (July 31, 2018) 84 | 85 | - Optimization for operands.rs. 86 | 87 | ## intl_pluralrules 0.8.1 (July 30, 2018) 88 | 89 | - Locale field. 90 | - Locale field getter function. 91 | - Update rules.rs. 92 | 93 | ## intl_pluralrules 0.8.0 (July 30, 2018) 94 | 95 | - Initial release. 96 | -------------------------------------------------------------------------------- /intl_pluralrules/Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "intl_pluralrules" 3 | version = "0.1.0" 4 | dependencies = [ 5 | "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 6 | ] 7 | 8 | [[package]] 9 | name = "matches" 10 | version = "0.1.6" 11 | source = "registry+https://github.com/rust-lang/crates.io-index" 12 | 13 | [metadata] 14 | "checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376" 15 | -------------------------------------------------------------------------------- /intl_pluralrules/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "intl_pluralrules" 3 | version = "7.0.2" 4 | edition = "2018" 5 | authors = ["Kekoa Riggin ", "Zibi Braniecki "] 6 | description = "Unicode Plural Rules categorizer for numeric input." 7 | license = "Apache-2.0/MIT" 8 | repository = "https://github.com/zbraniecki/pluralrules" 9 | readme = "README.md" 10 | keywords = ["localization", "l10n", "i18n", "intl", "internationalization"] 11 | categories = ["localization", "internationalization"] 12 | include = [ 13 | "src/**/*", 14 | "benches/*.rs", 15 | "Cargo.toml", 16 | "README.md" 17 | ] 18 | 19 | [badges] 20 | travis-ci = { repository = "zbraniecki/pluralrules", branch = "master" } 21 | coveralls = { repository = "zbraniecki/pluralrules", branch = "master", service = "github" } 22 | 23 | maintenance = { status = "actively-developed" } 24 | 25 | [dependencies] 26 | unic-langid = "0.9" 27 | 28 | [dev-dependencies] 29 | criterion = "0.3" 30 | unic-langid = { version = "0.9", features = ["macros"] } 31 | 32 | [[bench]] 33 | name = "pluralrules" 34 | harness = false 35 | -------------------------------------------------------------------------------- /intl_pluralrules/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /intl_pluralrules/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /intl_pluralrules/README.md: -------------------------------------------------------------------------------- 1 | # INTL Plural Rules 2 | 3 | `intl_pluralrules` categorizes numbers by plural operands. See [Unicode Plural Rules](https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules) 4 | 5 | 6 | [![crates.io](https://img.shields.io/crates/v/intl_pluralrules.svg)](https://crates.io/crates/intl_pluralrules) 7 | [![Build Status](https://travis-ci.org/zbraniecki/pluralrules.svg?branch=master)](https://travis-ci.org/zbraniecki/pluralrules) 8 | [![Coverage Status](https://coveralls.io/repos/github/zbraniecki/pluralrules/badge.svg?branch=master)](https://coveralls.io/github/zbraniecki/pluralrules?branch=master) 9 | 10 | This library is intended to be used to find the plural category of numeric input. 11 | 12 | Status 13 | ------ 14 | 15 | Currently produces operands compliant with CLDR 36 into Rust 1.31 and above. 16 | 17 | **Updating CLDR Data** 18 | 19 | If you would like to update rules.rs with plural rules that are not the specified version above (e.g. future versions of CLDR or external CLDR-compliant rules), you can regenerate the logic in rules.rs with the command: 20 | 21 | cargo regenerate-data 22 | 23 | You will need to replace the JSON files under `/cldr_data/` with your new CLDR JSON files. 24 | 25 | Local Development 26 | ----------------- 27 | 28 | cargo build 29 | cargo test 30 | 31 | When submitting a PR please use `cargo fmt`. 32 | 33 | Contributors 34 | ------------ 35 | 36 | * [manishearth](https://github.com/manishearth) 37 | 38 | Thank you to all contributors! 39 | 40 | [CLDR]: https://cldr.unicode.org/ 41 | [PluralRules]: https://cldr.unicode.org/index/cldr-spec/plural-rules 42 | [LDML Language Plural Rules Syntax]: https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules 43 | -------------------------------------------------------------------------------- /intl_pluralrules/benches/pluralrules.rs: -------------------------------------------------------------------------------- 1 | use criterion::criterion_group; 2 | use criterion::criterion_main; 3 | use criterion::BenchmarkId; 4 | use criterion::Criterion; 5 | 6 | use intl_pluralrules::{PluralRuleType, PluralRules}; 7 | use unic_langid::{langid, LanguageIdentifier}; 8 | 9 | fn plural_rules(c: &mut Criterion) { 10 | let langs = &["uk", "de", "sk", "ar", "fr", "it", "en", "cs", "es", "zh"]; 11 | let langs: Vec = langs 12 | .iter() 13 | .map(|l| l.parse().expect("Parsing failed")) 14 | .collect(); 15 | 16 | c.bench_with_input( 17 | BenchmarkId::new("construct", langs.len()), 18 | &langs, 19 | |b, langs| { 20 | b.iter(|| { 21 | for lang in langs { 22 | PluralRules::create(lang.clone(), PluralRuleType::ORDINAL).unwrap(); 23 | PluralRules::create(lang.clone(), PluralRuleType::CARDINAL).unwrap(); 24 | } 25 | }); 26 | }, 27 | ); 28 | 29 | let samples = &[ 30 | 1, 2, 3, 4, 5, 25, 134, 910293019, 12, 1412, -12, 15, 2931, 31231, 3123, 13231, 91, 0, 231, 31 | -2, -45, 33, 728, 2, 291, 24, 479, 291, 778, 919, 93, 32 | ]; 33 | 34 | let langid_pl = langid!("pl"); 35 | let ipr = PluralRules::create(langid_pl.clone(), PluralRuleType::CARDINAL).unwrap(); 36 | 37 | c.bench_with_input( 38 | BenchmarkId::new("select", samples.len()), 39 | samples, 40 | |b, samples| { 41 | b.iter(|| { 42 | for value in samples { 43 | ipr.select(*value).unwrap(); 44 | } 45 | }); 46 | }, 47 | ); 48 | 49 | c.bench_function("total", |b| { 50 | b.iter(|| { 51 | let ipr = PluralRules::create(langid_pl.clone(), PluralRuleType::CARDINAL).unwrap(); 52 | ipr.select(1).unwrap(); 53 | ipr.select(2).unwrap(); 54 | ipr.select(3).unwrap(); 55 | ipr.select(4).unwrap(); 56 | ipr.select(5).unwrap(); 57 | ipr.select(25).unwrap(); 58 | ipr.select(134).unwrap(); 59 | ipr.select(5090).unwrap(); 60 | ipr.select(910293019).unwrap(); 61 | ipr.select(5.2).unwrap(); 62 | ipr.select(-0.2).unwrap(); 63 | ipr.select("12.06").unwrap(); 64 | }) 65 | }); 66 | } 67 | 68 | criterion_group!(benches, plural_rules,); 69 | criterion_main!(benches); 70 | -------------------------------------------------------------------------------- /intl_pluralrules/cldr_data/cldr_pluralrules_ordinals.json: -------------------------------------------------------------------------------- 1 | { 2 | "supplemental": { 3 | "version": { 4 | "_unicodeVersion": "13.0.0", 5 | "_cldrVersion": "37" 6 | }, 7 | "plurals-type-ordinal": { 8 | "af": { 9 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 10 | }, 11 | "am": { 12 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 13 | }, 14 | "an": { 15 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 16 | }, 17 | "ar": { 18 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 19 | }, 20 | "as": { 21 | "pluralRule-count-one": "n = 1,5,7,8,9,10 @integer 1, 5, 7~10", 22 | "pluralRule-count-two": "n = 2,3 @integer 2, 3", 23 | "pluralRule-count-few": "n = 4 @integer 4", 24 | "pluralRule-count-many": "n = 6 @integer 6", 25 | "pluralRule-count-other": " @integer 0, 11~25, 100, 1000, 10000, 100000, 1000000, …" 26 | }, 27 | "az": { 28 | "pluralRule-count-one": "i % 10 = 1,2,5,7,8 or i % 100 = 20,50,70,80 @integer 1, 2, 5, 7, 8, 11, 12, 15, 17, 18, 20~22, 25, 101, 1001, …", 29 | "pluralRule-count-few": "i % 10 = 3,4 or i % 1000 = 100,200,300,400,500,600,700,800,900 @integer 3, 4, 13, 14, 23, 24, 33, 34, 43, 44, 53, 54, 63, 64, 73, 74, 100, 1003, …", 30 | "pluralRule-count-many": "i = 0 or i % 10 = 6 or i % 100 = 40,60,90 @integer 0, 6, 16, 26, 36, 40, 46, 56, 106, 1006, …", 31 | "pluralRule-count-other": " @integer 9, 10, 19, 29, 30, 39, 49, 59, 69, 79, 109, 1000, 10000, 100000, 1000000, …" 32 | }, 33 | "be": { 34 | "pluralRule-count-few": "n % 10 = 2,3 and n % 100 != 12,13 @integer 2, 3, 22, 23, 32, 33, 42, 43, 52, 53, 62, 63, 72, 73, 82, 83, 102, 1002, …", 35 | "pluralRule-count-other": " @integer 0, 1, 4~17, 100, 1000, 10000, 100000, 1000000, …" 36 | }, 37 | "bg": { 38 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 39 | }, 40 | "bn": { 41 | "pluralRule-count-one": "n = 1,5,7,8,9,10 @integer 1, 5, 7~10", 42 | "pluralRule-count-two": "n = 2,3 @integer 2, 3", 43 | "pluralRule-count-few": "n = 4 @integer 4", 44 | "pluralRule-count-many": "n = 6 @integer 6", 45 | "pluralRule-count-other": " @integer 0, 11~25, 100, 1000, 10000, 100000, 1000000, …" 46 | }, 47 | "bs": { 48 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 49 | }, 50 | "ca": { 51 | "pluralRule-count-one": "n = 1,3 @integer 1, 3", 52 | "pluralRule-count-two": "n = 2 @integer 2", 53 | "pluralRule-count-few": "n = 4 @integer 4", 54 | "pluralRule-count-other": " @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …" 55 | }, 56 | "ce": { 57 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 58 | }, 59 | "cs": { 60 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 61 | }, 62 | "cy": { 63 | "pluralRule-count-zero": "n = 0,7,8,9 @integer 0, 7~9", 64 | "pluralRule-count-one": "n = 1 @integer 1", 65 | "pluralRule-count-two": "n = 2 @integer 2", 66 | "pluralRule-count-few": "n = 3,4 @integer 3, 4", 67 | "pluralRule-count-many": "n = 5,6 @integer 5, 6", 68 | "pluralRule-count-other": " @integer 10~25, 100, 1000, 10000, 100000, 1000000, …" 69 | }, 70 | "da": { 71 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 72 | }, 73 | "de": { 74 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 75 | }, 76 | "dsb": { 77 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 78 | }, 79 | "el": { 80 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 81 | }, 82 | "en": { 83 | "pluralRule-count-one": "n % 10 = 1 and n % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …", 84 | "pluralRule-count-two": "n % 10 = 2 and n % 100 != 12 @integer 2, 22, 32, 42, 52, 62, 72, 82, 102, 1002, …", 85 | "pluralRule-count-few": "n % 10 = 3 and n % 100 != 13 @integer 3, 23, 33, 43, 53, 63, 73, 83, 103, 1003, …", 86 | "pluralRule-count-other": " @integer 0, 4~18, 100, 1000, 10000, 100000, 1000000, …" 87 | }, 88 | "es": { 89 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 90 | }, 91 | "et": { 92 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 93 | }, 94 | "eu": { 95 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 96 | }, 97 | "fa": { 98 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 99 | }, 100 | "fi": { 101 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 102 | }, 103 | "fil": { 104 | "pluralRule-count-one": "n = 1 @integer 1", 105 | "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 106 | }, 107 | "fr": { 108 | "pluralRule-count-one": "n = 1 @integer 1", 109 | "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 110 | }, 111 | "fy": { 112 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 113 | }, 114 | "ga": { 115 | "pluralRule-count-one": "n = 1 @integer 1", 116 | "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 117 | }, 118 | "gd": { 119 | "pluralRule-count-one": "n = 1,11 @integer 1, 11", 120 | "pluralRule-count-two": "n = 2,12 @integer 2, 12", 121 | "pluralRule-count-few": "n = 3,13 @integer 3, 13", 122 | "pluralRule-count-other": " @integer 0, 4~10, 14~21, 100, 1000, 10000, 100000, 1000000, …" 123 | }, 124 | "gl": { 125 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 126 | }, 127 | "gsw": { 128 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 129 | }, 130 | "gu": { 131 | "pluralRule-count-one": "n = 1 @integer 1", 132 | "pluralRule-count-two": "n = 2,3 @integer 2, 3", 133 | "pluralRule-count-few": "n = 4 @integer 4", 134 | "pluralRule-count-many": "n = 6 @integer 6", 135 | "pluralRule-count-other": " @integer 0, 5, 7~20, 100, 1000, 10000, 100000, 1000000, …" 136 | }, 137 | "he": { 138 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 139 | }, 140 | "hi": { 141 | "pluralRule-count-one": "n = 1 @integer 1", 142 | "pluralRule-count-two": "n = 2,3 @integer 2, 3", 143 | "pluralRule-count-few": "n = 4 @integer 4", 144 | "pluralRule-count-many": "n = 6 @integer 6", 145 | "pluralRule-count-other": " @integer 0, 5, 7~20, 100, 1000, 10000, 100000, 1000000, …" 146 | }, 147 | "hr": { 148 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 149 | }, 150 | "hsb": { 151 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 152 | }, 153 | "hu": { 154 | "pluralRule-count-one": "n = 1,5 @integer 1, 5", 155 | "pluralRule-count-other": " @integer 0, 2~4, 6~17, 100, 1000, 10000, 100000, 1000000, …" 156 | }, 157 | "hy": { 158 | "pluralRule-count-one": "n = 1 @integer 1", 159 | "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 160 | }, 161 | "ia": { 162 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 163 | }, 164 | "id": { 165 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 166 | }, 167 | "in": { 168 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 169 | }, 170 | "is": { 171 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 172 | }, 173 | "it": { 174 | "pluralRule-count-many": "n = 11,8,80,800 @integer 8, 11, 80, 800", 175 | "pluralRule-count-other": " @integer 0~7, 9, 10, 12~17, 100, 1000, 10000, 100000, 1000000, …" 176 | }, 177 | "iw": { 178 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 179 | }, 180 | "ja": { 181 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 182 | }, 183 | "ka": { 184 | "pluralRule-count-one": "i = 1 @integer 1", 185 | "pluralRule-count-many": "i = 0 or i % 100 = 2..20,40,60,80 @integer 0, 2~16, 102, 1002, …", 186 | "pluralRule-count-other": " @integer 21~36, 100, 1000, 10000, 100000, 1000000, …" 187 | }, 188 | "kk": { 189 | "pluralRule-count-many": "n % 10 = 6 or n % 10 = 9 or n % 10 = 0 and n != 0 @integer 6, 9, 10, 16, 19, 20, 26, 29, 30, 36, 39, 40, 100, 1000, 10000, 100000, 1000000, …", 190 | "pluralRule-count-other": " @integer 0~5, 7, 8, 11~15, 17, 18, 21, 101, 1001, …" 191 | }, 192 | "km": { 193 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 194 | }, 195 | "kn": { 196 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 197 | }, 198 | "ko": { 199 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 200 | }, 201 | "kw": { 202 | "pluralRule-count-one": "n = 1..4 or n % 100 = 1..4,21..24,41..44,61..64,81..84 @integer 1~4, 21~24, 41~44, 61~64, 101, 1001, …", 203 | "pluralRule-count-many": "n = 5 or n % 100 = 5 @integer 5, 105, 205, 305, 405, 505, 605, 705, 1005, …", 204 | "pluralRule-count-other": " @integer 0, 6~20, 100, 1000, 10000, 100000, 1000000, …" 205 | }, 206 | "ky": { 207 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 208 | }, 209 | "lo": { 210 | "pluralRule-count-one": "n = 1 @integer 1", 211 | "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 212 | }, 213 | "lt": { 214 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 215 | }, 216 | "lv": { 217 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 218 | }, 219 | "mk": { 220 | "pluralRule-count-one": "i % 10 = 1 and i % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …", 221 | "pluralRule-count-two": "i % 10 = 2 and i % 100 != 12 @integer 2, 22, 32, 42, 52, 62, 72, 82, 102, 1002, …", 222 | "pluralRule-count-many": "i % 10 = 7,8 and i % 100 != 17,18 @integer 7, 8, 27, 28, 37, 38, 47, 48, 57, 58, 67, 68, 77, 78, 87, 88, 107, 1007, …", 223 | "pluralRule-count-other": " @integer 0, 3~6, 9~19, 100, 1000, 10000, 100000, 1000000, …" 224 | }, 225 | "ml": { 226 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 227 | }, 228 | "mn": { 229 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 230 | }, 231 | "mo": { 232 | "pluralRule-count-one": "n = 1 @integer 1", 233 | "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 234 | }, 235 | "mr": { 236 | "pluralRule-count-one": "n = 1 @integer 1", 237 | "pluralRule-count-two": "n = 2,3 @integer 2, 3", 238 | "pluralRule-count-few": "n = 4 @integer 4", 239 | "pluralRule-count-other": " @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …" 240 | }, 241 | "ms": { 242 | "pluralRule-count-one": "n = 1 @integer 1", 243 | "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 244 | }, 245 | "my": { 246 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 247 | }, 248 | "nb": { 249 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 250 | }, 251 | "ne": { 252 | "pluralRule-count-one": "n = 1..4 @integer 1~4", 253 | "pluralRule-count-other": " @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …" 254 | }, 255 | "nl": { 256 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 257 | }, 258 | "or": { 259 | "pluralRule-count-one": "n = 1,5,7..9 @integer 1, 5, 7~9", 260 | "pluralRule-count-two": "n = 2,3 @integer 2, 3", 261 | "pluralRule-count-few": "n = 4 @integer 4", 262 | "pluralRule-count-many": "n = 6 @integer 6", 263 | "pluralRule-count-other": " @integer 0, 10~24, 100, 1000, 10000, 100000, 1000000, …" 264 | }, 265 | "pa": { 266 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 267 | }, 268 | "pl": { 269 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 270 | }, 271 | "prg": { 272 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 273 | }, 274 | "ps": { 275 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 276 | }, 277 | "pt": { 278 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 279 | }, 280 | "ro": { 281 | "pluralRule-count-one": "n = 1 @integer 1", 282 | "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 283 | }, 284 | "root": { 285 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 286 | }, 287 | "ru": { 288 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 289 | }, 290 | "sc": { 291 | "pluralRule-count-many": "n = 11,8,80,800 @integer 8, 11, 80, 800", 292 | "pluralRule-count-other": " @integer 0~7, 9, 10, 12~17, 100, 1000, 10000, 100000, 1000000, …" 293 | }, 294 | "scn": { 295 | "pluralRule-count-many": "n = 11,8,80,800 @integer 8, 11, 80, 800", 296 | "pluralRule-count-other": " @integer 0~7, 9, 10, 12~17, 100, 1000, 10000, 100000, 1000000, …" 297 | }, 298 | "sd": { 299 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 300 | }, 301 | "sh": { 302 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 303 | }, 304 | "si": { 305 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 306 | }, 307 | "sk": { 308 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 309 | }, 310 | "sl": { 311 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 312 | }, 313 | "sq": { 314 | "pluralRule-count-one": "n = 1 @integer 1", 315 | "pluralRule-count-many": "n % 10 = 4 and n % 100 != 14 @integer 4, 24, 34, 44, 54, 64, 74, 84, 104, 1004, …", 316 | "pluralRule-count-other": " @integer 0, 2, 3, 5~17, 100, 1000, 10000, 100000, 1000000, …" 317 | }, 318 | "sr": { 319 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 320 | }, 321 | "sv": { 322 | "pluralRule-count-one": "n % 10 = 1,2 and n % 100 != 11,12 @integer 1, 2, 21, 22, 31, 32, 41, 42, 51, 52, 61, 62, 71, 72, 81, 82, 101, 1001, …", 323 | "pluralRule-count-other": " @integer 0, 3~17, 100, 1000, 10000, 100000, 1000000, …" 324 | }, 325 | "sw": { 326 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 327 | }, 328 | "ta": { 329 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 330 | }, 331 | "te": { 332 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 333 | }, 334 | "th": { 335 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 336 | }, 337 | "tk": { 338 | "pluralRule-count-few": "n % 10 = 6,9 or n = 10 @integer 6, 9, 10, 16, 19, 26, 29, 36, 39, 106, 1006, …", 339 | "pluralRule-count-other": " @integer 0~5, 7, 8, 11~15, 17, 18, 20, 100, 1000, 10000, 100000, 1000000, …" 340 | }, 341 | "tl": { 342 | "pluralRule-count-one": "n = 1 @integer 1", 343 | "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 344 | }, 345 | "tr": { 346 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 347 | }, 348 | "uk": { 349 | "pluralRule-count-few": "n % 10 = 3 and n % 100 != 13 @integer 3, 23, 33, 43, 53, 63, 73, 83, 103, 1003, …", 350 | "pluralRule-count-other": " @integer 0~2, 4~16, 100, 1000, 10000, 100000, 1000000, …" 351 | }, 352 | "ur": { 353 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 354 | }, 355 | "uz": { 356 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 357 | }, 358 | "vi": { 359 | "pluralRule-count-one": "n = 1 @integer 1", 360 | "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 361 | }, 362 | "yue": { 363 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 364 | }, 365 | "zh": { 366 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 367 | }, 368 | "zu": { 369 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 370 | } 371 | } 372 | } 373 | } 374 | -------------------------------------------------------------------------------- /intl_pluralrules/src/lib.rs: -------------------------------------------------------------------------------- 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](https://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(not(tarpaulin_include))] 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 | /// ``` 101 | pub fn create>( 102 | langid: L, 103 | prt: PluralRuleType, 104 | ) -> Result { 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 | /// ``` 140 | pub fn select>( 141 | &self, 142 | number: N, 143 | ) -> Result { 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 | /// ``` 163 | pub fn get_locales(prt: PluralRuleType) -> Vec { 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 | /// ``` 182 | 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] 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] 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] 214 | fn version_test() { 215 | assert_eq!(CLDR_VERSION, 37); 216 | } 217 | 218 | #[test] 219 | fn locale_test() { 220 | assert_eq!( 221 | PluralRules::get_locales(PluralRuleType::CARDINAL).is_empty(), 222 | false 223 | ); 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /intl_pluralrules/src/operands.rs: -------------------------------------------------------------------------------- 1 | //! Plural operands in compliance with [CLDR Plural Rules](https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules). 2 | //! 3 | //! See [full operands description](https://unicode.org/reports/tr35/tr35-numbers.html#Operands). 4 | //! 5 | //! # Examples 6 | //! 7 | //! From int 8 | //! 9 | //! ``` 10 | //! use std::convert::TryFrom; 11 | //! use intl_pluralrules::operands::*; 12 | //! assert_eq!(Ok(PluralOperands { 13 | //! n: 2_f64, 14 | //! i: 2, 15 | //! v: 0, 16 | //! w: 0, 17 | //! f: 0, 18 | //! t: 0, 19 | //! }), PluralOperands::try_from(2)) 20 | //! ``` 21 | //! 22 | //! From float 23 | //! 24 | //! ``` 25 | //! use std::convert::TryFrom; 26 | //! use intl_pluralrules::operands::*; 27 | //! assert_eq!(Ok(PluralOperands { 28 | //! n: 1234.567_f64, 29 | //! i: 1234, 30 | //! v: 3, 31 | //! w: 3, 32 | //! f: 567, 33 | //! t: 567, 34 | //! }), PluralOperands::try_from("-1234.567")) 35 | //! ``` 36 | //! 37 | //! From &str 38 | //! 39 | //! ``` 40 | //! use std::convert::TryFrom; 41 | //! use intl_pluralrules::operands::*; 42 | //! assert_eq!(Ok(PluralOperands { 43 | //! n: 123.45_f64, 44 | //! i: 123, 45 | //! v: 2, 46 | //! w: 2, 47 | //! f: 45, 48 | //! t: 45, 49 | //! }), PluralOperands::try_from(123.45)) 50 | //! ``` 51 | #![cfg_attr(feature = "cargo-clippy", allow(clippy::cast_lossless))] 52 | use std::convert::TryFrom; 53 | use std::isize; 54 | use std::str::FromStr; 55 | 56 | /// A full plural operands representation of a number. See [CLDR Plural Rules](https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules) for complete operands description. 57 | #[derive(Debug, PartialEq)] 58 | pub struct PluralOperands { 59 | /// Absolute value of input 60 | pub n: f64, 61 | /// Integer value of input 62 | pub i: u64, 63 | /// Number of visible fraction digits with trailing zeros 64 | pub v: usize, 65 | /// Number of visible fraction digits without trailing zeros 66 | pub w: usize, 67 | /// Visible fraction digits with trailing zeros 68 | pub f: u64, 69 | /// Visible fraction digits without trailing zeros 70 | pub t: u64, 71 | } 72 | 73 | impl<'a> TryFrom<&'a str> for PluralOperands { 74 | type Error = &'static str; 75 | 76 | fn try_from(input: &'a str) -> Result { 77 | let abs_str = if input.starts_with('-') { 78 | &input[1..] 79 | } else { 80 | &input 81 | }; 82 | 83 | let absolute_value = f64::from_str(&abs_str).map_err(|_| "Incorrect number passed!")?; 84 | 85 | let integer_digits; 86 | let num_fraction_digits0; 87 | let num_fraction_digits; 88 | let fraction_digits0; 89 | let fraction_digits; 90 | 91 | if let Some(dec_pos) = abs_str.find('.') { 92 | let int_str = &abs_str[..dec_pos]; 93 | let dec_str = &abs_str[(dec_pos + 1)..]; 94 | 95 | integer_digits = 96 | u64::from_str(&int_str).map_err(|_| "Could not convert string to integer!")?; 97 | 98 | let backtrace = dec_str.trim_end_matches('0'); 99 | 100 | num_fraction_digits0 = dec_str.len() as usize; 101 | num_fraction_digits = backtrace.len() as usize; 102 | fraction_digits0 = 103 | u64::from_str(dec_str).map_err(|_| "Could not convert string to integer!")?; 104 | fraction_digits = u64::from_str(backtrace).unwrap_or(0); 105 | } else { 106 | integer_digits = absolute_value as u64; 107 | num_fraction_digits0 = 0; 108 | num_fraction_digits = 0; 109 | fraction_digits0 = 0; 110 | fraction_digits = 0; 111 | } 112 | 113 | Ok(PluralOperands { 114 | n: absolute_value, 115 | i: integer_digits, 116 | v: num_fraction_digits0, 117 | w: num_fraction_digits, 118 | f: fraction_digits0, 119 | t: fraction_digits, 120 | }) 121 | } 122 | } 123 | 124 | macro_rules! impl_integer_type { 125 | ($ty:ident) => { 126 | impl From<$ty> for PluralOperands { 127 | fn from(input: $ty) -> Self { 128 | // XXXManishearth converting from u32 or u64 to isize may wrap 129 | PluralOperands { 130 | n: input as f64, 131 | i: input as u64, 132 | v: 0, 133 | w: 0, 134 | f: 0, 135 | t: 0, 136 | } 137 | } 138 | } 139 | }; 140 | ($($ty:ident)+) => { 141 | $(impl_integer_type!($ty);)+ 142 | }; 143 | } 144 | 145 | macro_rules! impl_signed_integer_type { 146 | ($ty:ident) => { 147 | impl TryFrom<$ty> for PluralOperands { 148 | type Error = &'static str; 149 | fn try_from(input: $ty) -> Result { 150 | // XXXManishearth converting from i64 to isize may wrap 151 | let x = (input as isize).checked_abs().ok_or("Number too big")?; 152 | Ok(PluralOperands { 153 | n: x as f64, 154 | i: x as u64, 155 | v: 0, 156 | w: 0, 157 | f: 0, 158 | t: 0, 159 | }) 160 | } 161 | } 162 | }; 163 | ($($ty:ident)+) => { 164 | $(impl_signed_integer_type!($ty);)+ 165 | }; 166 | } 167 | 168 | macro_rules! impl_convert_type { 169 | ($ty:ident) => { 170 | impl TryFrom<$ty> for PluralOperands { 171 | type Error = &'static str; 172 | fn try_from(input: $ty) -> Result { 173 | let as_str: &str = &input.to_string(); 174 | PluralOperands::try_from(as_str) 175 | } 176 | } 177 | }; 178 | ($($ty:ident)+) => { 179 | $(impl_convert_type!($ty);)+ 180 | }; 181 | } 182 | 183 | impl_integer_type!(u8 u16 u32 u64 usize); 184 | impl_signed_integer_type!(i8 i16 i32 i64 isize); 185 | // XXXManishearth we can likely have dedicated float impls here 186 | impl_convert_type!(f32 f64 String); 187 | -------------------------------------------------------------------------------- /intl_pluralrules/tests/operands.rs: -------------------------------------------------------------------------------- 1 | // Still want to test 2 | // Non-numeric input 3 | // Empty Input 4 | 5 | use std::convert::{TryFrom, TryInto}; 6 | 7 | use intl_pluralrules::{operands::*, PluralRuleType, PluralRules}; 8 | use unic_langid::LanguageIdentifier; 9 | 10 | #[test] 11 | fn test_operands_from_str() { 12 | let tests = vec![ 13 | ((0_f64, 0, 0, 0, 0, 0), "0"), 14 | ((2_f64, 2, 0, 0, 0, 0), "2"), 15 | ((57_f64, 57, 0, 0, 0, 0), "57"), 16 | ((987_f64, 987, 0, 0, 0, 0), "987"), 17 | ((1234567_f64, 1234567, 0, 0, 0, 0), "1234567"), 18 | ((10_f64, 10, 0, 0, 0, 0), "-10"), 19 | ((1000000_f64, 1000000, 0, 0, 0, 0), "-1000000"), 20 | ((0.23_f64, 0, 2, 2, 23, 23), "0.23"), 21 | ((0.230_f64, 0, 3, 2, 230, 23), "0.230"), 22 | ((23.00_f64, 23, 2, 0, 00, 0), "23.00"), 23 | ((0.0203000_f64, 0, 7, 4, 203000, 203), "0.0203000"), 24 | ((123.45_f64, 123, 2, 2, 45, 45), "123.45"), 25 | ((1234.567_f64, 1234, 3, 3, 567, 567), "-1234.567"), 26 | ]; 27 | 28 | for test in tests { 29 | assert_eq!( 30 | Ok(PluralOperands { 31 | n: (test.0).0, 32 | i: (test.0).1, 33 | v: (test.0).2, 34 | w: (test.0).3, 35 | f: (test.0).4, 36 | t: (test.0).5, 37 | }), 38 | PluralOperands::try_from(test.1) 39 | ); 40 | } 41 | } 42 | 43 | #[test] 44 | fn test_operands_from_int() { 45 | let tests = vec![ 46 | ((0_f64, 0, 0, 0, 0, 0), 0), 47 | ((2_f64, 2, 0, 0, 0, 0), 2), 48 | ((57_f64, 57, 0, 0, 0, 0), 57), 49 | ((987_f64, 987, 0, 0, 0, 0), 987), 50 | ((1234567_f64, 1234567, 0, 0, 0, 0), 1234567), 51 | ((10_f64, 10, 0, 0, 0, 0), -10), 52 | ((1000000_f64, 1000000, 0, 0, 0, 0), -1000000), 53 | ]; 54 | 55 | for test in tests { 56 | assert_eq!( 57 | Ok(PluralOperands { 58 | n: (test.0).0, 59 | i: (test.0).1, 60 | v: (test.0).2, 61 | w: (test.0).3, 62 | f: (test.0).4, 63 | t: (test.0).5, 64 | }), 65 | PluralOperands::try_from(test.1) 66 | ); 67 | } 68 | } 69 | 70 | #[test] 71 | fn test_operands_from_float() { 72 | let tests = vec![ 73 | ((0.23_f64, 0, 2, 2, 23, 23), 0.23), 74 | ((0.230_f64, 0, 2, 2, 23, 23), 0.230), 75 | ((0.0203000_f64, 0, 4, 4, 203, 203), 0.0203000), 76 | ((123.45_f64, 123, 2, 2, 45, 45), 123.45), 77 | ((1234.567_f64, 1234, 3, 3, 567, 567), -1234.567), 78 | ]; 79 | 80 | for test in tests { 81 | assert_eq!( 82 | Ok(PluralOperands { 83 | n: (test.0).0, 84 | i: (test.0).1, 85 | v: (test.0).2, 86 | w: (test.0).3, 87 | f: (test.0).4, 88 | t: (test.0).5, 89 | }), 90 | PluralOperands::try_from(test.1) 91 | ); 92 | } 93 | } 94 | 95 | #[test] 96 | fn test_incorrect_operand() { 97 | assert_eq!(PluralOperands::try_from("foo").is_err(), true); 98 | } 99 | 100 | #[test] 101 | fn test_get_locale() { 102 | let langid: LanguageIdentifier = "naq".parse().expect("Parsing failed."); 103 | let pr_naq = PluralRules::create(langid.clone(), PluralRuleType::CARDINAL).unwrap(); 104 | assert_eq!(pr_naq.get_locale(), &langid); 105 | } 106 | 107 | #[test] 108 | fn custom_type() { 109 | use intl_pluralrules::{PluralCategory, PluralRuleType, PluralRules}; 110 | struct MyType { 111 | value: isize, 112 | } 113 | 114 | impl TryInto for MyType { 115 | type Error = &'static str; 116 | fn try_into(self) -> Result { 117 | Ok(PluralOperands { 118 | n: self.value as f64, 119 | i: self.value as u64, 120 | v: 0, 121 | w: 0, 122 | f: 0, 123 | t: 0, 124 | }) 125 | } 126 | } 127 | 128 | let langid: LanguageIdentifier = "en".parse().expect("Parsing failed."); 129 | let pr = PluralRules::create(langid, PluralRuleType::CARDINAL).unwrap(); 130 | let v = MyType { value: 5 }; 131 | 132 | assert_eq!(pr.select(v), Ok(PluralCategory::OTHER)); 133 | } 134 | 135 | #[test] 136 | fn many_decimal_places() { 137 | // this should not panic on an i32 system 138 | let num: f64 = 2.813829837982735; 139 | assert_eq!(dbg!(PluralOperands::try_from(num)).is_ok(), true); 140 | } 141 | -------------------------------------------------------------------------------- /make_pluralrules/.cargo/config: -------------------------------------------------------------------------------- 1 | [alias] 2 | regenerate_fixtures = "run -- -i ./tests/fixtures/cldr_pluralrules_cardinals_33.json ./tests/fixtures/cldr_pluralrules_ordinals_33.json -o ./tests/fixtures/cldr_pluralrules_33.rs -u" 3 | regenerate_fixtures_within = "run -- -i ./tests/fixtures/cldr_pluralrules_within_test.json -o ./tests/fixtures/cldr_pluralrules_within_test.rs -u" 4 | -------------------------------------------------------------------------------- /make_pluralrules/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Unreleased 4 | 5 | - … 6 | 7 | ## make_pluralrules 0.5.0 (November 13, 2019) 8 | 9 | - Update to `quote` and `proc-macro2` to 1.0. 10 | - Remove `phf_codegen` use. 11 | - Move the codegen to generate a sorted vector of tuples of 12 | (`LanguageIdentifier`, PluralRule). 13 | - Update CLDR PluralRules Parser 2.0. 14 | 15 | ## make_pluralrules 0.4.2 (January 7, 2019) 16 | 17 | - Update to Rust 2018. 18 | 19 | ## make_pluralrules 0.4.1 (October 26, 2018) 20 | 21 | - Update the generated rules to use references. 22 | 23 | ## make_pluralrules 0.4.0 (August 15, 2018) 24 | 25 | - Add fixtures regenerator 26 | - Use PHF for generated matches 27 | - Update generated code 28 | - Update fixtures 29 | 30 | ## make_pluralrules 0.3.3 (August 6, 2018) 31 | 32 | - Remove PluralRuleType from generated output and adapt tests. 33 | 34 | ## make_pluralrules 0.3.2 (August 3, 2018) 35 | 36 | - Test coverage. 37 | 38 | ## make_pluralrules 0.3.1 (July 30, 2018) 39 | 40 | - Negate matches for ranges. 41 | 42 | ## make_pluralrules 0.3.0 (July 26, 2018) 43 | 44 | - Arguments with Clap 45 | - Multiple file input 46 | - Handling for both cardinal and oridinal plural rules 47 | - All changes in gen_pr.rs 48 | - rustfmt on output 49 | 50 | ## make_pluralrules 0.2.0 (July 25, 2018) 51 | 52 | - Fix docs. 53 | 54 | ## make_pluralrules 0.1.0 (July 25, 2018) 55 | 56 | - Initial release. 57 | -------------------------------------------------------------------------------- /make_pluralrules/Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "adler32" 3 | version = "1.0.3" 4 | source = "registry+https://github.com/rust-lang/crates.io-index" 5 | 6 | [[package]] 7 | name = "arrayvec" 8 | version = "0.4.7" 9 | source = "registry+https://github.com/rust-lang/crates.io-index" 10 | dependencies = [ 11 | "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 12 | ] 13 | 14 | [[package]] 15 | name = "base64" 16 | version = "0.9.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | dependencies = [ 19 | "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 20 | "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 21 | ] 22 | 23 | [[package]] 24 | name = "bitflags" 25 | version = "0.9.1" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | 28 | [[package]] 29 | name = "bitflags" 30 | version = "1.0.3" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | 33 | [[package]] 34 | name = "build_const" 35 | version = "0.2.1" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | 38 | [[package]] 39 | name = "byteorder" 40 | version = "1.2.3" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | 43 | [[package]] 44 | name = "bytes" 45 | version = "0.4.8" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | dependencies = [ 48 | "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 49 | "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 50 | ] 51 | 52 | [[package]] 53 | name = "cc" 54 | version = "1.0.18" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | 57 | [[package]] 58 | name = "cfg-if" 59 | version = "0.1.4" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | 62 | [[package]] 63 | name = "cldr_pluralrules_parser" 64 | version = "0.1.0" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | dependencies = [ 67 | "nom 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 68 | ] 69 | 70 | [[package]] 71 | name = "core-foundation" 72 | version = "0.2.3" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | dependencies = [ 75 | "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 76 | "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", 77 | ] 78 | 79 | [[package]] 80 | name = "core-foundation-sys" 81 | version = "0.2.3" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | dependencies = [ 84 | "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", 85 | ] 86 | 87 | [[package]] 88 | name = "crc" 89 | version = "1.8.1" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | dependencies = [ 92 | "build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 93 | ] 94 | 95 | [[package]] 96 | name = "crossbeam-deque" 97 | version = "0.3.1" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | dependencies = [ 100 | "crossbeam-epoch 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 101 | "crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 102 | ] 103 | 104 | [[package]] 105 | name = "crossbeam-epoch" 106 | version = "0.4.3" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | dependencies = [ 109 | "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", 110 | "cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 111 | "crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 112 | "lazy_static 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 113 | "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 114 | "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 115 | ] 116 | 117 | [[package]] 118 | name = "crossbeam-utils" 119 | version = "0.3.2" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | dependencies = [ 122 | "cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 123 | ] 124 | 125 | [[package]] 126 | name = "dtoa" 127 | version = "0.4.3" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | 130 | [[package]] 131 | name = "encoding_rs" 132 | version = "0.7.2" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | dependencies = [ 135 | "cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 136 | ] 137 | 138 | [[package]] 139 | name = "foreign-types" 140 | version = "0.3.2" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | dependencies = [ 143 | "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 144 | ] 145 | 146 | [[package]] 147 | name = "foreign-types-shared" 148 | version = "0.1.1" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | 151 | [[package]] 152 | name = "fuchsia-zircon" 153 | version = "0.3.3" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | dependencies = [ 156 | "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 157 | "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 158 | ] 159 | 160 | [[package]] 161 | name = "fuchsia-zircon-sys" 162 | version = "0.3.3" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | 165 | [[package]] 166 | name = "futures" 167 | version = "0.1.23" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | 170 | [[package]] 171 | name = "futures-cpupool" 172 | version = "0.1.8" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | dependencies = [ 175 | "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", 176 | "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 177 | ] 178 | 179 | [[package]] 180 | name = "httparse" 181 | version = "1.3.2" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | 184 | [[package]] 185 | name = "hyper" 186 | version = "0.11.27" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | dependencies = [ 189 | "base64 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", 190 | "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 191 | "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", 192 | "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 193 | "httparse 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 194 | "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 195 | "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 196 | "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 197 | "mime 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 198 | "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 199 | "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 200 | "relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 201 | "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", 202 | "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", 203 | "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 204 | "tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 205 | "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 206 | "unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 207 | "want 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 208 | ] 209 | 210 | [[package]] 211 | name = "hyper-tls" 212 | version = "0.1.3" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | dependencies = [ 215 | "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", 216 | "hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)", 217 | "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 218 | "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", 219 | "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 220 | "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 221 | "tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 222 | ] 223 | 224 | [[package]] 225 | name = "idna" 226 | version = "0.1.5" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | dependencies = [ 229 | "matches 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 230 | "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 231 | "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 232 | ] 233 | 234 | [[package]] 235 | name = "iovec" 236 | version = "0.1.2" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | dependencies = [ 239 | "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", 240 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 241 | ] 242 | 243 | [[package]] 244 | name = "itoa" 245 | version = "0.4.2" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | 248 | [[package]] 249 | name = "kernel32-sys" 250 | version = "0.2.2" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | dependencies = [ 253 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 254 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 255 | ] 256 | 257 | [[package]] 258 | name = "language-tags" 259 | version = "0.2.2" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | 262 | [[package]] 263 | name = "lazy_static" 264 | version = "0.2.11" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | 267 | [[package]] 268 | name = "lazy_static" 269 | version = "1.0.2" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | 272 | [[package]] 273 | name = "lazycell" 274 | version = "0.6.0" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | 277 | [[package]] 278 | name = "libc" 279 | version = "0.2.42" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | 282 | [[package]] 283 | name = "libflate" 284 | version = "0.1.16" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | dependencies = [ 287 | "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 288 | "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 289 | "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", 290 | ] 291 | 292 | [[package]] 293 | name = "log" 294 | version = "0.3.9" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | dependencies = [ 297 | "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 298 | ] 299 | 300 | [[package]] 301 | name = "log" 302 | version = "0.4.3" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | dependencies = [ 305 | "cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 306 | ] 307 | 308 | [[package]] 309 | name = "make_pluralrules" 310 | version = "0.1.0" 311 | dependencies = [ 312 | "cldr_pluralrules_parser 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 313 | "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 314 | "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", 315 | "reqwest 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", 316 | "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", 317 | "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", 318 | "serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", 319 | ] 320 | 321 | [[package]] 322 | name = "matches" 323 | version = "0.1.7" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | 326 | [[package]] 327 | name = "memchr" 328 | version = "2.0.1" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | dependencies = [ 331 | "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", 332 | ] 333 | 334 | [[package]] 335 | name = "memoffset" 336 | version = "0.2.1" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | 339 | [[package]] 340 | name = "mime" 341 | version = "0.3.8" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | dependencies = [ 344 | "unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 345 | ] 346 | 347 | [[package]] 348 | name = "mime_guess" 349 | version = "2.0.0-alpha.6" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | dependencies = [ 352 | "mime 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 353 | "phf 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", 354 | "phf_codegen 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", 355 | "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 356 | ] 357 | 358 | [[package]] 359 | name = "mio" 360 | version = "0.6.15" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | dependencies = [ 363 | "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 364 | "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 365 | "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 366 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 367 | "lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 368 | "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", 369 | "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 370 | "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 371 | "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 372 | "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 373 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 374 | ] 375 | 376 | [[package]] 377 | name = "miow" 378 | version = "0.2.1" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | dependencies = [ 381 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 382 | "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 383 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 384 | "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 385 | ] 386 | 387 | [[package]] 388 | name = "native-tls" 389 | version = "0.1.5" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | dependencies = [ 392 | "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 393 | "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", 394 | "openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", 395 | "schannel 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", 396 | "security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", 397 | "security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", 398 | "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 399 | ] 400 | 401 | [[package]] 402 | name = "net2" 403 | version = "0.2.33" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | dependencies = [ 406 | "cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 407 | "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", 408 | "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 409 | ] 410 | 411 | [[package]] 412 | name = "nodrop" 413 | version = "0.1.12" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | 416 | [[package]] 417 | name = "nom" 418 | version = "4.0.0" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | dependencies = [ 421 | "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 422 | ] 423 | 424 | [[package]] 425 | name = "num_cpus" 426 | version = "1.8.0" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | dependencies = [ 429 | "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", 430 | ] 431 | 432 | [[package]] 433 | name = "openssl" 434 | version = "0.9.24" 435 | source = "registry+https://github.com/rust-lang/crates.io-index" 436 | dependencies = [ 437 | "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", 438 | "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 439 | "lazy_static 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 440 | "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", 441 | "openssl-sys 0.9.33 (registry+https://github.com/rust-lang/crates.io-index)", 442 | ] 443 | 444 | [[package]] 445 | name = "openssl-sys" 446 | version = "0.9.33" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | dependencies = [ 449 | "cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", 450 | "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", 451 | "pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", 452 | "vcpkg 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", 453 | ] 454 | 455 | [[package]] 456 | name = "percent-encoding" 457 | version = "1.0.1" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | 460 | [[package]] 461 | name = "phf" 462 | version = "0.7.22" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | dependencies = [ 465 | "phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", 466 | ] 467 | 468 | [[package]] 469 | name = "phf_codegen" 470 | version = "0.7.22" 471 | source = "registry+https://github.com/rust-lang/crates.io-index" 472 | dependencies = [ 473 | "phf_generator 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", 474 | "phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", 475 | ] 476 | 477 | [[package]] 478 | name = "phf_generator" 479 | version = "0.7.22" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | dependencies = [ 482 | "phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", 483 | "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 484 | ] 485 | 486 | [[package]] 487 | name = "phf_shared" 488 | version = "0.7.22" 489 | source = "registry+https://github.com/rust-lang/crates.io-index" 490 | dependencies = [ 491 | "siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 492 | "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 493 | ] 494 | 495 | [[package]] 496 | name = "pkg-config" 497 | version = "0.3.12" 498 | source = "registry+https://github.com/rust-lang/crates.io-index" 499 | 500 | [[package]] 501 | name = "proc-macro2" 502 | version = "0.4.6" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | dependencies = [ 505 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 506 | ] 507 | 508 | [[package]] 509 | name = "quote" 510 | version = "0.6.3" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | dependencies = [ 513 | "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 514 | ] 515 | 516 | [[package]] 517 | name = "rand" 518 | version = "0.3.22" 519 | source = "registry+https://github.com/rust-lang/crates.io-index" 520 | dependencies = [ 521 | "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 522 | "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", 523 | "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 524 | ] 525 | 526 | [[package]] 527 | name = "rand" 528 | version = "0.4.2" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | dependencies = [ 531 | "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 532 | "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", 533 | "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 534 | ] 535 | 536 | [[package]] 537 | name = "redox_syscall" 538 | version = "0.1.40" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | 541 | [[package]] 542 | name = "relay" 543 | version = "0.1.1" 544 | source = "registry+https://github.com/rust-lang/crates.io-index" 545 | dependencies = [ 546 | "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", 547 | ] 548 | 549 | [[package]] 550 | name = "remove_dir_all" 551 | version = "0.5.1" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | dependencies = [ 554 | "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 555 | ] 556 | 557 | [[package]] 558 | name = "reqwest" 559 | version = "0.8.6" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | dependencies = [ 562 | "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 563 | "encoding_rs 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 564 | "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", 565 | "hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)", 566 | "hyper-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 567 | "libflate 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", 568 | "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 569 | "mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", 570 | "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 571 | "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", 572 | "serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", 573 | "serde_urlencoded 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 574 | "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", 575 | "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 576 | "tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 577 | "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 578 | "uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 579 | ] 580 | 581 | [[package]] 582 | name = "safemem" 583 | version = "0.2.0" 584 | source = "registry+https://github.com/rust-lang/crates.io-index" 585 | 586 | [[package]] 587 | name = "schannel" 588 | version = "0.1.13" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | dependencies = [ 591 | "lazy_static 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 592 | "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 593 | ] 594 | 595 | [[package]] 596 | name = "scoped-tls" 597 | version = "0.1.2" 598 | source = "registry+https://github.com/rust-lang/crates.io-index" 599 | 600 | [[package]] 601 | name = "scopeguard" 602 | version = "0.3.3" 603 | source = "registry+https://github.com/rust-lang/crates.io-index" 604 | 605 | [[package]] 606 | name = "security-framework" 607 | version = "0.1.16" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | dependencies = [ 610 | "core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 611 | "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 612 | "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", 613 | "security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", 614 | ] 615 | 616 | [[package]] 617 | name = "security-framework-sys" 618 | version = "0.1.16" 619 | source = "registry+https://github.com/rust-lang/crates.io-index" 620 | dependencies = [ 621 | "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 622 | "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", 623 | ] 624 | 625 | [[package]] 626 | name = "serde" 627 | version = "1.0.70" 628 | source = "registry+https://github.com/rust-lang/crates.io-index" 629 | 630 | [[package]] 631 | name = "serde_derive" 632 | version = "1.0.70" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | dependencies = [ 635 | "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 636 | "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", 637 | "syn 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)", 638 | ] 639 | 640 | [[package]] 641 | name = "serde_json" 642 | version = "1.0.22" 643 | source = "registry+https://github.com/rust-lang/crates.io-index" 644 | dependencies = [ 645 | "dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 646 | "itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 647 | "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", 648 | ] 649 | 650 | [[package]] 651 | name = "serde_urlencoded" 652 | version = "0.5.2" 653 | source = "registry+https://github.com/rust-lang/crates.io-index" 654 | dependencies = [ 655 | "dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 656 | "itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 657 | "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", 658 | "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 659 | ] 660 | 661 | [[package]] 662 | name = "siphasher" 663 | version = "0.2.2" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | 666 | [[package]] 667 | name = "slab" 668 | version = "0.3.0" 669 | source = "registry+https://github.com/rust-lang/crates.io-index" 670 | 671 | [[package]] 672 | name = "slab" 673 | version = "0.4.0" 674 | source = "registry+https://github.com/rust-lang/crates.io-index" 675 | 676 | [[package]] 677 | name = "smallvec" 678 | version = "0.2.1" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | 681 | [[package]] 682 | name = "syn" 683 | version = "0.14.4" 684 | source = "registry+https://github.com/rust-lang/crates.io-index" 685 | dependencies = [ 686 | "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 687 | "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", 688 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 689 | ] 690 | 691 | [[package]] 692 | name = "take" 693 | version = "0.1.0" 694 | source = "registry+https://github.com/rust-lang/crates.io-index" 695 | 696 | [[package]] 697 | name = "tempdir" 698 | version = "0.3.7" 699 | source = "registry+https://github.com/rust-lang/crates.io-index" 700 | dependencies = [ 701 | "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 702 | "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 703 | ] 704 | 705 | [[package]] 706 | name = "time" 707 | version = "0.1.40" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | dependencies = [ 710 | "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", 711 | "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", 712 | "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 713 | ] 714 | 715 | [[package]] 716 | name = "tokio" 717 | version = "0.1.7" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | dependencies = [ 720 | "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", 721 | "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", 722 | "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 723 | "tokio-fs 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 724 | "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 725 | "tokio-reactor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 726 | "tokio-tcp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 727 | "tokio-threadpool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 728 | "tokio-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", 729 | "tokio-udp 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 730 | ] 731 | 732 | [[package]] 733 | name = "tokio-codec" 734 | version = "0.1.0" 735 | source = "registry+https://github.com/rust-lang/crates.io-index" 736 | dependencies = [ 737 | "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 738 | "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", 739 | "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 740 | ] 741 | 742 | [[package]] 743 | name = "tokio-core" 744 | version = "0.1.17" 745 | source = "registry+https://github.com/rust-lang/crates.io-index" 746 | dependencies = [ 747 | "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 748 | "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", 749 | "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 750 | "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 751 | "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", 752 | "scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 753 | "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 754 | "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 755 | "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 756 | "tokio-reactor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 757 | "tokio-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", 758 | ] 759 | 760 | [[package]] 761 | name = "tokio-executor" 762 | version = "0.1.2" 763 | source = "registry+https://github.com/rust-lang/crates.io-index" 764 | dependencies = [ 765 | "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", 766 | ] 767 | 768 | [[package]] 769 | name = "tokio-fs" 770 | version = "0.1.2" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | dependencies = [ 773 | "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", 774 | "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 775 | "tokio-threadpool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 776 | ] 777 | 778 | [[package]] 779 | name = "tokio-io" 780 | version = "0.1.7" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | dependencies = [ 783 | "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 784 | "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", 785 | "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 786 | ] 787 | 788 | [[package]] 789 | name = "tokio-proto" 790 | version = "0.1.1" 791 | source = "registry+https://github.com/rust-lang/crates.io-index" 792 | dependencies = [ 793 | "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", 794 | "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 795 | "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 796 | "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", 797 | "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 798 | "smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 799 | "take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 800 | "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", 801 | "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 802 | "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 803 | ] 804 | 805 | [[package]] 806 | name = "tokio-reactor" 807 | version = "0.1.2" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | dependencies = [ 810 | "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", 811 | "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 812 | "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", 813 | "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 814 | "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 815 | "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 816 | ] 817 | 818 | [[package]] 819 | name = "tokio-service" 820 | version = "0.1.0" 821 | source = "registry+https://github.com/rust-lang/crates.io-index" 822 | dependencies = [ 823 | "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", 824 | ] 825 | 826 | [[package]] 827 | name = "tokio-tcp" 828 | version = "0.1.0" 829 | source = "registry+https://github.com/rust-lang/crates.io-index" 830 | dependencies = [ 831 | "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 832 | "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", 833 | "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 834 | "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", 835 | "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 836 | "tokio-reactor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 837 | ] 838 | 839 | [[package]] 840 | name = "tokio-threadpool" 841 | version = "0.1.5" 842 | source = "registry+https://github.com/rust-lang/crates.io-index" 843 | dependencies = [ 844 | "crossbeam-deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 845 | "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", 846 | "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 847 | "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 848 | "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 849 | "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 850 | ] 851 | 852 | [[package]] 853 | name = "tokio-timer" 854 | version = "0.2.4" 855 | source = "registry+https://github.com/rust-lang/crates.io-index" 856 | dependencies = [ 857 | "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", 858 | "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 859 | ] 860 | 861 | [[package]] 862 | name = "tokio-tls" 863 | version = "0.1.4" 864 | source = "registry+https://github.com/rust-lang/crates.io-index" 865 | dependencies = [ 866 | "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", 867 | "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 868 | "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", 869 | "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 870 | ] 871 | 872 | [[package]] 873 | name = "tokio-udp" 874 | version = "0.1.1" 875 | source = "registry+https://github.com/rust-lang/crates.io-index" 876 | dependencies = [ 877 | "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 878 | "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", 879 | "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 880 | "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", 881 | "tokio-codec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 882 | "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 883 | "tokio-reactor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 884 | ] 885 | 886 | [[package]] 887 | name = "try-lock" 888 | version = "0.1.0" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | 891 | [[package]] 892 | name = "unicase" 893 | version = "1.4.2" 894 | source = "registry+https://github.com/rust-lang/crates.io-index" 895 | dependencies = [ 896 | "version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 897 | ] 898 | 899 | [[package]] 900 | name = "unicase" 901 | version = "2.1.0" 902 | source = "registry+https://github.com/rust-lang/crates.io-index" 903 | dependencies = [ 904 | "version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 905 | ] 906 | 907 | [[package]] 908 | name = "unicode-bidi" 909 | version = "0.3.4" 910 | source = "registry+https://github.com/rust-lang/crates.io-index" 911 | dependencies = [ 912 | "matches 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 913 | ] 914 | 915 | [[package]] 916 | name = "unicode-normalization" 917 | version = "0.1.7" 918 | source = "registry+https://github.com/rust-lang/crates.io-index" 919 | 920 | [[package]] 921 | name = "unicode-xid" 922 | version = "0.1.0" 923 | source = "registry+https://github.com/rust-lang/crates.io-index" 924 | 925 | [[package]] 926 | name = "url" 927 | version = "1.7.1" 928 | source = "registry+https://github.com/rust-lang/crates.io-index" 929 | dependencies = [ 930 | "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 931 | "matches 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 932 | "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 933 | ] 934 | 935 | [[package]] 936 | name = "uuid" 937 | version = "0.6.5" 938 | source = "registry+https://github.com/rust-lang/crates.io-index" 939 | dependencies = [ 940 | "cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 941 | "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 942 | ] 943 | 944 | [[package]] 945 | name = "vcpkg" 946 | version = "0.2.4" 947 | source = "registry+https://github.com/rust-lang/crates.io-index" 948 | 949 | [[package]] 950 | name = "version_check" 951 | version = "0.1.4" 952 | source = "registry+https://github.com/rust-lang/crates.io-index" 953 | 954 | [[package]] 955 | name = "want" 956 | version = "0.0.4" 957 | source = "registry+https://github.com/rust-lang/crates.io-index" 958 | dependencies = [ 959 | "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", 960 | "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 961 | "try-lock 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 962 | ] 963 | 964 | [[package]] 965 | name = "winapi" 966 | version = "0.2.8" 967 | source = "registry+https://github.com/rust-lang/crates.io-index" 968 | 969 | [[package]] 970 | name = "winapi" 971 | version = "0.3.5" 972 | source = "registry+https://github.com/rust-lang/crates.io-index" 973 | dependencies = [ 974 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 975 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 976 | ] 977 | 978 | [[package]] 979 | name = "winapi-build" 980 | version = "0.1.1" 981 | source = "registry+https://github.com/rust-lang/crates.io-index" 982 | 983 | [[package]] 984 | name = "winapi-i686-pc-windows-gnu" 985 | version = "0.4.0" 986 | source = "registry+https://github.com/rust-lang/crates.io-index" 987 | 988 | [[package]] 989 | name = "winapi-x86_64-pc-windows-gnu" 990 | version = "0.4.0" 991 | source = "registry+https://github.com/rust-lang/crates.io-index" 992 | 993 | [[package]] 994 | name = "ws2_32-sys" 995 | version = "0.2.1" 996 | source = "registry+https://github.com/rust-lang/crates.io-index" 997 | dependencies = [ 998 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 999 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 1000 | ] 1001 | 1002 | [metadata] 1003 | "checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" 1004 | "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" 1005 | "checksum base64 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "85415d2594767338a74a30c1d370b2f3262ec1b4ed2d7bba5b3faf4de40467d9" 1006 | "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" 1007 | "checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" 1008 | "checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" 1009 | "checksum byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "74c0b906e9446b0a2e4f760cdb3fa4b2c48cdc6db8766a845c54b6ff063fd2e9" 1010 | "checksum bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7dd32989a66957d3f0cba6588f15d4281a733f4e9ffc43fcd2385f57d3bf99ff" 1011 | "checksum cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "2119ea4867bd2b8ed3aecab467709720b2d55b1bcfe09f772fd68066eaf15275" 1012 | "checksum cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efe5c877e17a9c717a0bf3613b2709f723202c4e4675cc8f12926ded29bcb17e" 1013 | "checksum cldr_pluralrules_parser 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5578022e58cce776e5e615b2b7684547ba4cde3d4f66da82d3a1eff84fa992f8" 1014 | "checksum core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25bfd746d203017f7d5cbd31ee5d8e17f94b6521c7af77ece6c9e4b2d4b16c67" 1015 | "checksum core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "065a5d7ffdcbc8fa145d6f0746f3555025b9097a9e9cda59f7467abae670c78d" 1016 | "checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" 1017 | "checksum crossbeam-deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fe8153ef04a7594ded05b427ffad46ddeaf22e63fd48d42b3e1e3bb4db07cae7" 1018 | "checksum crossbeam-epoch 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2af0e75710d6181e234c8ecc79f14a97907850a541b13b0be1dd10992f2e4620" 1019 | "checksum crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d636a8b3bcc1b409d7ffd3facef8f21dcb4009626adbd0c5e6c4305c07253c7b" 1020 | "checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd" 1021 | "checksum encoding_rs 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98fd0f24d1fb71a4a6b9330c8ca04cbd4e7cc5d846b54ca74ff376bc7c9f798d" 1022 | "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 1023 | "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 1024 | "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 1025 | "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 1026 | "checksum futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "884dbe32a6ae4cd7da5c6db9b78114449df9953b8d490c9d7e1b51720b922c62" 1027 | "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" 1028 | "checksum httparse 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7b6288d7db100340ca12873fd4d08ad1b8f206a9457798dfb17c018a33fee540" 1029 | "checksum hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)" = "34a590ca09d341e94cddf8e5af0bbccde205d5fbc2fa3c09dd67c7f85cea59d7" 1030 | "checksum hyper-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a5aa51f6ae9842239b0fac14af5f22123b8432b4cc774a44ff059fcba0f675ca" 1031 | "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" 1032 | "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" 1033 | "checksum itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5adb58558dcd1d786b5f0bd15f3226ee23486e24b7b58304b60f64dc68e62606" 1034 | "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 1035 | "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" 1036 | "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" 1037 | "checksum lazy_static 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fb497c35d362b6a331cfd94956a07fc2c78a4604cdbee844a81170386b996dd3" 1038 | "checksum lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a6f08839bc70ef4a3fe1d566d5350f519c5912ea86be0df1740a7d247c7fc0ef" 1039 | "checksum libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)" = "b685088df2b950fccadf07a7187c8ef846a959c142338a48f9dc0b94517eb5f1" 1040 | "checksum libflate 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7d4b4c7aff5bac19b956f693d0ea0eade8066deb092186ae954fa6ba14daab98" 1041 | "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" 1042 | "checksum log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "61bd98ae7f7b754bc53dca7d44b604f733c6bba044ea6f41bc8d89272d8161d2" 1043 | "checksum matches 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "835511bab37c34c47da5cb44844bea2cfde0236db0b506f90ea4224482c9774a" 1044 | "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" 1045 | "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" 1046 | "checksum mime 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fe51c8699d2dc522bf8c1ebe26ea2193d151fb54bcdfd7d0318750c189994cd9" 1047 | "checksum mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "30de2e4613efcba1ec63d8133f344076952090c122992a903359be5a4f99c3ed" 1048 | "checksum mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)" = "4fcfcb32d63961fb6f367bfd5d21e4600b92cd310f71f9dca25acae196eb1560" 1049 | "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" 1050 | "checksum native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f74dbadc8b43df7864539cedb7bc91345e532fdd913cfdc23ad94f4d2d40fbc0" 1051 | "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" 1052 | "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" 1053 | "checksum nom 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "898696750eb5c3ce5eb5afbfbe46e7f7c4e1936e19d3e97be4b7937da7b6d114" 1054 | "checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" 1055 | "checksum openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)" = "a3605c298474a3aa69de92d21139fb5e2a81688d308262359d85cdd0d12a7985" 1056 | "checksum openssl-sys 0.9.33 (registry+https://github.com/rust-lang/crates.io-index)" = "d8abc04833dcedef24221a91852931df2f63e3369ae003134e70aff3645775cc" 1057 | "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" 1058 | "checksum phf 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "7d37a244c75a9748e049225155f56dbcb98fe71b192fd25fd23cb914b5ad62f2" 1059 | "checksum phf_codegen 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "4e4048fe7dd7a06b8127ecd6d3803149126e9b33c7558879846da3a63f734f2b" 1060 | "checksum phf_generator 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "05a079dd052e7b674d21cb31cbb6c05efd56a2cd2827db7692e2f1a507ebd998" 1061 | "checksum phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "c2261d544c2bb6aa3b10022b0be371b9c7c64f762ef28c6f5d4f1ef6d97b5930" 1062 | "checksum pkg-config 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "6a52e4dbc8354505ee07e484ab07127e06d87ca6fa7f0a516a2b294e5ad5ad16" 1063 | "checksum proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "effdb53b25cdad54f8f48843d67398f7ef2e14f12c1b4cb4effc549a6462a4d6" 1064 | "checksum quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e44651a0dc4cdd99f71c83b561e221f714912d11af1a4dff0631f923d53af035" 1065 | "checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" 1066 | "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" 1067 | "checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" 1068 | "checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a" 1069 | "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" 1070 | "checksum reqwest 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2abe46f8e00792693a2488e296c593d1f4ea39bb1178cfce081d6793657575e4" 1071 | "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" 1072 | "checksum schannel 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "dc1fabf2a7b6483a141426e1afd09ad543520a77ac49bd03c286e7696ccfd77f" 1073 | "checksum scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" 1074 | "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" 1075 | "checksum security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "dfa44ee9c54ce5eecc9de7d5acbad112ee58755239381f687e564004ba4a2332" 1076 | "checksum security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "5421621e836278a0b139268f36eee0dc7e389b784dc3f79d8f11aabadf41bead" 1077 | "checksum serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)" = "0c3adf19c07af6d186d91dae8927b83b0553d07ca56cbf7f2f32560455c91920" 1078 | "checksum serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)" = "3525a779832b08693031b8ecfb0de81cd71cfd3812088fafe9a7496789572124" 1079 | "checksum serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)" = "84b8035cabe9b35878adec8ac5fe03d5f6bc97ff6edd7ccb96b44c1276ba390e" 1080 | "checksum serde_urlencoded 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e703cef904312097cfceab9ce131ff6bbe09e8c964a0703345a5f49238757bc1" 1081 | "checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537" 1082 | "checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" 1083 | "checksum slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdeff4cd9ecff59ec7e3744cbca73dfe5ac35c2aedb2cfba8a1c715a18912e9d" 1084 | "checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013" 1085 | "checksum syn 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)" = "2beff8ebc3658f07512a413866875adddd20f4fd47b2a4e6c9da65cd281baaea" 1086 | "checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5" 1087 | "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" 1088 | "checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" 1089 | "checksum tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8ee337e5f4e501fc32966fec6fe0ca0cc1c237b0b1b14a335f8bfe3c5f06e286" 1090 | "checksum tokio-codec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "881e9645b81c2ce95fcb799ded2c29ffb9f25ef5bef909089a420e5961dd8ccb" 1091 | "checksum tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "aeeffbbb94209023feaef3c196a41cbcdafa06b4a6f893f68779bb5e53796f71" 1092 | "checksum tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8cac2a7883ff3567e9d66bb09100d09b33d90311feca0206c7ca034bc0c55113" 1093 | "checksum tokio-fs 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "40697ecbea5660df15b15d50a077386477d2f6a35002adf01ce76ff9dd9dce48" 1094 | "checksum tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a5c9635ee806f26d302b8baa1e145689a280d8f5aa8d0552e7344808da54cc21" 1095 | "checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389" 1096 | "checksum tokio-reactor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e00ec63bbec2c97ce1178cb0587b2c438b2f6b09d3ee54a33c45a9cf0d530810" 1097 | "checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" 1098 | "checksum tokio-tcp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec9b094851aadd2caf83ba3ad8e8c4ce65a42104f7b94d9e6550023f0407853f" 1099 | "checksum tokio-threadpool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "24ab84f574027b0e875378f31575cf175360891919e93a3490f07e76e00e4efb" 1100 | "checksum tokio-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "028b94314065b90f026a21826cffd62a4e40a92cda3e5c069cc7b02e5945f5e9" 1101 | "checksum tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "772f4b04e560117fe3b0a53e490c16ddc8ba6ec437015d91fa385564996ed913" 1102 | "checksum tokio-udp 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "43eb534af6e8f37d43ab1b612660df14755c42bd003c5f8d2475ee78cc4600c0" 1103 | "checksum try-lock 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee2aa4715743892880f70885373966c83d73ef1b0838a664ef0c76fffd35e7c2" 1104 | "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" 1105 | "checksum unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284b6d3db520d67fbe88fd778c21510d1b0ba4a551e5d0fbb023d33405f6de8a" 1106 | "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 1107 | "checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" 1108 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 1109 | "checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6" 1110 | "checksum uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e1436e58182935dcd9ce0add9ea0b558e8a87befe01c1a301e6020aeb0876363" 1111 | "checksum vcpkg 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cbe533e138811704c0e3cbde65a818b35d3240409b4346256c5ede403e082474" 1112 | "checksum version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7716c242968ee87e5542f8021178248f267f295a5c4803beae8b8b7fd9bc6051" 1113 | "checksum want 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a05d9d966753fa4b5c8db73fcab5eed4549cfe0e1e4e66911e5564a0085c35d1" 1114 | "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 1115 | "checksum winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "773ef9dcc5f24b7d850d0ff101e542ff24c3b090a9768e03ff889fdef41f00fd" 1116 | "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 1117 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1118 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1119 | "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 1120 | -------------------------------------------------------------------------------- /make_pluralrules/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "make_pluralrules" 3 | version = "0.5.0" 4 | edition = "2018" 5 | authors = ["Kekoa Riggin ", "Zibi Braniecki "] 6 | description = "A Rustcode generator for CLDR plural rules." 7 | license = "Apache-2.0/MIT" 8 | repository = "https://github.com/unclenachoduh/pluralrules" 9 | readme = "README.md" 10 | keywords = ["localization", "l10n", "i18n", "intl", "internationalization"] 11 | categories = ["localization", "internationalization"] 12 | include = [ 13 | "src/**/*", 14 | "benches/*.rs", 15 | "Cargo.toml", 16 | "README.md" 17 | ] 18 | 19 | [badges] 20 | travis-ci = { repository = "zbraniecki/pluralrules", branch = "master" } 21 | coveralls = { repository = "zbraniecki/pluralrules", branch = "master", service = "github" } 22 | 23 | maintenance = { status = "actively-developed" } 24 | 25 | [dependencies] 26 | cldr_pluralrules_parser = "2.0" 27 | serde = { version = "1.0", features = ["derive"] } 28 | serde_json = "1.0" 29 | quote = "1.0" 30 | proc-macro2 = "1.0" 31 | clap = "2.33" 32 | unic-langid = "0.8" 33 | -------------------------------------------------------------------------------- /make_pluralrules/README.md: -------------------------------------------------------------------------------- 1 | # Make Plural Rules 2 | 3 | `make_pluralrules` is a code generator application that turns a [Unicode CLDR plural rules](https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules) AST into Rust code. 4 | 5 | [![crates.io](https://img.shields.io/crates/v/make_pluralrules.svg)](https://crates.io/crates/make_pluralrules) 6 | [![Build Status](https://travis-ci.org/zbraniecki/pluralrules.svg?branch=master)](https://travis-ci.org/zbraniecki/pluralrules) 7 | [![Coverage Status](https://coveralls.io/repos/github/zbraniecki/pluralrules/badge.svg?branch=master)](https://coveralls.io/github/zbraniecki/pluralrules?branch=master) 8 | 9 | The application is intended to generate code necessary for calculating correct plural rules categories. 10 | 11 | Status 12 | ------ 13 | 14 | The generator currently generates code for cardinal plural rules from CLDR 34 into Rust 1.31 and above. 15 | 16 | Launch make_pluralrules with: 17 | 18 | ``` 19 | cargo run -- -i <./path/to/cldr.json>... -o <./path/to/output.rs> 20 | ``` 21 | 22 | Local Development 23 | ----------------- 24 | 25 | cargo build 26 | cargo test 27 | 28 | If you want to update the test fixtures to match your latest changes, please use: 29 | 30 | cargo regenerate_fixtures_within | cargo regenerate_fixtures 31 | 32 | When submitting a PR please use `cargo fmt`. 33 | 34 | Contributors 35 | ------------ 36 | 37 | * [manishearth](https://github.com/manishearth) 38 | 39 | Thank you to all contributors! 40 | 41 | [CLDR]: https://cldr.unicode.org/ 42 | [PluralRules]: https://cldr.unicode.org/index/cldr-spec/plural-rules 43 | [LDML Language Plural Rules Syntax]: https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules 44 | -------------------------------------------------------------------------------- /make_pluralrules/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! make_pluralrules generates a Rust code representation of CLDR plural rules in compliance with [Unicode](https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules). 2 | //! 3 | //! Representations of plural rules are generated from [Unicode's plural rules](https://github.com/unicode-cldr/cldr-core/blob/master/supplemental/plurals.json) and uses the intl_pluralrules_parser AST to build the representation. 4 | //! 5 | //! The ouput is a Rust file, specified by the user in the comand 6 | //! ```text 7 | //! cargo run -- -i <./path/to/cldr.json>... -o <./path/to/output.rs> 8 | //! ``` 9 | 10 | mod parser; 11 | 12 | use crate::parser::plural_category::PluralCategory; 13 | use crate::parser::resource::*; 14 | use proc_macro2::TokenStream; 15 | use std::collections::BTreeMap; 16 | use unic_langid::LanguageIdentifier; 17 | 18 | /// Takes a string representation of a CLDR JSON file and produces a string representation of the generated Rust code for the plural rules. 19 | /// 20 | /// The string representation of the Rust code is written to a specified Rust file and can be used to get the plural category for numerical input. 21 | pub fn generate_rs(cldr_jsons: &[String]) -> String { 22 | let mut cldr_version = None; 23 | let mut tokens = BTreeMap::new(); 24 | 25 | for cldr_json in cldr_jsons { 26 | // resource_items is a struct representation of the raw CLDR rules. 27 | let resource_items = parse_plurals_resource_from_string(cldr_json).unwrap(); 28 | 29 | let res_cldr_version = resource_items.supplemental.version.cldr_version; 30 | 31 | if cldr_version.is_none() { 32 | cldr_version = Some(res_cldr_version); 33 | } else if cldr_version != Some(res_cldr_version) { 34 | panic!("All input resources must use the same CLDR version!"); 35 | } 36 | 37 | if let Some(data) = resource_items.supplemental.plurals_type_cardinal { 38 | let rule_tokens = gen_type_rs(data); 39 | if tokens.contains_key("cardinal") { 40 | panic!("Cannot provide two inputs with the same data!"); 41 | } 42 | tokens.insert("cardinal".to_owned(), rule_tokens); 43 | } 44 | 45 | if let Some(data) = resource_items.supplemental.plurals_type_ordinal { 46 | let rule_tokens = gen_type_rs(data); 47 | if tokens.contains_key("ordinal") { 48 | panic!("Cannot provide two inputs with the same data!"); 49 | } 50 | tokens.insert("ordinal".to_owned(), rule_tokens); 51 | } 52 | } 53 | 54 | if cldr_version.is_none() || tokens.is_empty() { 55 | panic!("None of the input files provided core data!"); 56 | } 57 | 58 | // Call gen_rs to get Rust code. Convert TokenStream to string for file out. 59 | parser::gen_rs::gen_fn(tokens, &cldr_version.unwrap()).to_string() 60 | } 61 | 62 | fn gen_type_rs(rules: BTreeMap>) -> Vec { 63 | // rule_tokens is a vector of TokenStreams that represent the CLDR plural rules as Rust expressions. 64 | let mut rule_tokens = Vec::::new(); 65 | 66 | let mut rules: Vec<(LanguageIdentifier, BTreeMap)> = rules 67 | .into_iter() 68 | .filter_map(|(key, value)| { 69 | if key == "root" { 70 | None 71 | } else { 72 | let langid = key 73 | .parse() 74 | .unwrap_or_else(|_| panic!("Parsing {} failed", key)); 75 | Some((langid, value)) 76 | } 77 | }) 78 | .collect(); 79 | 80 | // We rely on sorted list for binary search in the consumer. 81 | rules.sort_unstable_by(|(langid1, _), (langid2, _)| langid1.cmp(langid2)); 82 | 83 | for (lang, r) in rules { 84 | // this_lang_rules is a vector of plural rules saved as a PluralCategory and a TokenStream 85 | let mut this_lang_rules = Vec::<(PluralCategory, TokenStream)>::new(); 86 | 87 | for (rule_name, rule_line) in r { 88 | // cat_name is the simplified category name from the CLDR source file 89 | let cat_name = rule_name.split('-').collect::>()[2]; 90 | 91 | // representation is the 92 | let representation = cldr_pluralrules_parser::parse_plural_condition(rule_line) 93 | .expect("Parsing of a condition succeeded"); 94 | 95 | let cat = if cat_name == "zero" { 96 | PluralCategory::ZERO 97 | } else if cat_name == "one" { 98 | PluralCategory::ONE 99 | } else if cat_name == "two" { 100 | PluralCategory::TWO 101 | } else if cat_name == "few" { 102 | PluralCategory::FEW 103 | } else if cat_name == "many" { 104 | PluralCategory::MANY 105 | } else { 106 | PluralCategory::OTHER 107 | }; 108 | 109 | // Only allow rules that are not `OTHER` to be added. `OTHER` can have no rules and is added outside of the loop. 110 | if cat != PluralCategory::OTHER { 111 | let tokens = parser::gen_pr::gen_pr(representation); 112 | this_lang_rules.push((cat, tokens)); 113 | } 114 | } 115 | // convert language rules to TokenStream and add them to all the rules 116 | rule_tokens.push(parser::gen_rs::gen_mid(&lang, &this_lang_rules)); 117 | } 118 | rule_tokens 119 | } 120 | -------------------------------------------------------------------------------- /make_pluralrules/src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::App; 2 | use make_pluralrules::generate_rs; 3 | use std::process::Command; 4 | 5 | use std::fs; 6 | use std::io::Write; 7 | 8 | fn main() -> std::io::Result<()> { 9 | let matches = App::new("CLDR Plural Rules Rust Generator") 10 | .version("0.1.0") 11 | .about("Generates Rust code for CLDR plural rules.") 12 | .args_from_usage( 13 | " -i, --input=... 'Input CLDR JSON plural rules files' 14 | -o, --output= 'Output RS file' 15 | [ugly] -u, --ugly", 16 | ) 17 | .get_matches(); 18 | 19 | let input_paths: Vec<&str> = matches.values_of("input-files").unwrap().collect(); 20 | 21 | let input_jsons = input_paths 22 | .iter() 23 | .map(|path| fs::read_to_string(path).expect("file not found")) 24 | .collect::>(); 25 | let complete_rs_code = generate_rs(&input_jsons); 26 | 27 | let output_path = matches.value_of("output-file").unwrap(); 28 | let mut file = fs::File::create(output_path)?; 29 | file.write_all(complete_rs_code.as_bytes())?; 30 | 31 | if !matches.is_present("ugly") { 32 | Command::new("rustfmt") 33 | .args(&[output_path]) 34 | .output() 35 | .expect("Failed to format the output using `rustfmt`"); 36 | } 37 | 38 | Ok(()) 39 | } 40 | -------------------------------------------------------------------------------- /make_pluralrules/src/parser/gen_pr.rs: -------------------------------------------------------------------------------- 1 | //! gen_pr contains functions that convert the CLDR Plural Rule AST into a token stream. 2 | //! 3 | //! The resulting token stream is used to generate a Rust code representation of CLDR plural rules. That Rust code is then used to determine the corresponding plural rule for a number. 4 | 5 | /// This code utilizes the cldr_pluralrules_parser AST 6 | use cldr_pluralrules_parser::ast::*; 7 | /// proc_macro2 provides the TokenStream type 8 | use proc_macro2::{Ident, Literal, Span, TokenStream}; 9 | use quote::quote; 10 | 11 | /// Convert a usize to a Literal 12 | fn convert_literal(num: usize) -> Literal { 13 | Literal::u64_unsuffixed(num as u64) 14 | } 15 | 16 | /// Convert a usize pair into a tuple of literals 17 | fn convert_range(low: usize, up: usize) -> (Literal, Literal) { 18 | let u = convert_literal(up); 19 | let d = convert_literal(low); 20 | 21 | (d, u) 22 | } 23 | 24 | /// Convert a range list into a tuple of lists: one for lists of values and one for lists of ranges 25 | fn convert_rangl(rangl: RangeList) -> (Vec, Vec<(Literal, Literal)>) { 26 | let mut litints = Vec::new(); 27 | let mut litrange = Vec::new(); 28 | 29 | for x in rangl.0 { 30 | match &x { 31 | RangeListItem::Value(x) => litints.push(convert_literal(x.0)), 32 | RangeListItem::Range(x) => litrange.push(convert_range(x.lower_val.0, x.upper_val.0)), 33 | } 34 | } 35 | 36 | (litints, litrange) 37 | } 38 | 39 | /// Match the needed operator symbol from Operator type in AST 40 | fn get_operator_symbol(op: &Operator) -> TokenStream { 41 | match op { 42 | Operator::In | Operator::Is | Operator::EQ => quote!(==), 43 | Operator::NotIn | Operator::IsNot | Operator::NotEQ => quote!(!=), 44 | Operator::Within => quote!(<=), 45 | Operator::NotWithin => quote!(>), 46 | } 47 | } 48 | 49 | /// Create a token stream representation from the AST Relation 50 | fn create_relation(rel: Relation) -> TokenStream { 51 | let left = rel.expression; 52 | let operator = rel.operator; 53 | let right = rel.range_list; 54 | 55 | // All relations that need to be represented in the rust code are folded here. They are unfolded into one token steam later. 56 | let mut relations = Vec::::new(); 57 | 58 | let l = match left.operand { 59 | Operand::N => "n", 60 | Operand::I => "i", 61 | Operand::V => "v", 62 | Operand::T => "t", 63 | Operand::W => "w", 64 | Operand::F => "f", 65 | }; 66 | let l = Ident::new(l, Span::call_site()); 67 | let o = get_operator_symbol(&operator); 68 | let r1 = convert_rangl(right); 69 | 70 | // If there is a modulus, convert to literal. If not, placehold literal 71 | let (mod_check, m) = if left.modulus != None { 72 | (true, convert_literal((left.modulus.unwrap().0).0)) 73 | } else { 74 | (false, convert_literal(0)) 75 | }; 76 | 77 | // Here, we have to manage 4 varying types of expression that we may write. 78 | 79 | // First, we check for a within operator because it changes the number of expressions we need. 80 | // Then, within that check, we look for the operand 'n'. Because 'n' can be a float, we may need to 81 | // change the compared value to a float type, or use the operand 'i' in 'n's place and also check for f == 0 82 | // We also check for a modulus on the operand. 83 | // Lastly, we want to make sure we allow values and ranges to be indeterminately folded together. 84 | 85 | // If within type operator, use format x < po && po < y 86 | if operator == Operator::Within || operator == Operator::NotWithin { 87 | let rfront = &(r1.1)[0].0; 88 | let rback = &(r1.1)[0].1; 89 | 90 | // Variants handled here 91 | let (rfront, rback, whole_symbol) = if left.operand == Operand::N { 92 | if !mod_check { 93 | (quote!(#rfront.0), quote!(#rback.0), quote!(po.#l)) 94 | } else { 95 | (quote!(#rfront), quote!(#rback), quote!(po.i % #m)) 96 | } 97 | } else { 98 | ( 99 | quote!(#rfront), 100 | quote!(#rback), 101 | if !mod_check { 102 | quote!(po.#l) 103 | } else { 104 | quote!(po.i % #m) 105 | }, 106 | ) 107 | }; 108 | 109 | let rel_tokens = quote! { #rfront #o #whole_symbol && #whole_symbol #o #rback}; 110 | relations.push(rel_tokens); 111 | // if not within type, use format po < x 112 | } else { 113 | // Recurisvely fold all values 114 | for r in r1.0 { 115 | // Variants handled here 116 | let (symbol, rval) = if left.operand == Operand::N { 117 | if !mod_check { 118 | (quote!(po.#l), quote!(#r.0)) 119 | } else { 120 | (quote!(po.i % #m), quote!(#r)) 121 | } 122 | } else { 123 | ( 124 | if !mod_check { 125 | quote!(po.#l) 126 | } else { 127 | quote!(po.#l % #m) 128 | }, 129 | quote!(#r), 130 | ) 131 | }; 132 | 133 | let rel_tokens = quote! { #symbol #o #rval }; 134 | relations.push(rel_tokens); 135 | } 136 | // Recursively fold all ranges 137 | for r in r1.1 { 138 | let rfront = r.0; 139 | let rback = r.1; 140 | 141 | // Variants handled here 142 | let (symbol, perim) = if left.operand == Operand::N { 143 | if !mod_check { 144 | (quote!(po.i), quote! { && po.f == 0}) 145 | } else { 146 | (quote!(po.i), quote! {}) 147 | } 148 | } else { 149 | ( 150 | if !mod_check { 151 | quote!(po.#l) 152 | } else { 153 | quote!(po.#l % #m) 154 | }, 155 | quote! {}, 156 | ) 157 | }; 158 | 159 | let rel_tokens = match operator { 160 | Operator::In | Operator::Is | Operator::EQ => { 161 | quote! { (#rfront ..= #rback).contains(&(#symbol)) #perim } 162 | } 163 | Operator::NotIn | Operator::NotEQ | Operator::IsNot => { 164 | quote! { !(#rfront ..= #rback).contains(&(#symbol)) #perim } 165 | } 166 | Operator::Within | Operator::NotWithin => { 167 | panic!("There was a problem with the source file.") 168 | } 169 | }; 170 | relations.push(rel_tokens); 171 | } 172 | } 173 | 174 | // Unfold all expressions from folded expressions 175 | match operator { 176 | Operator::In | Operator::Is | Operator::EQ => { 177 | if relations.len() > 1 { 178 | quote! { ( #(#relations)||* ) } 179 | } else { 180 | quote! { #(#relations)||* } 181 | } 182 | } 183 | Operator::NotIn | Operator::NotEQ | Operator::IsNot => quote! { #(#relations)&&* }, 184 | Operator::Within | Operator::NotWithin => quote! { #(#relations)||* }, 185 | } 186 | } 187 | 188 | // Unfold AndConditions and tokenize together with && 189 | fn create_and_condition(acond: AndCondition) -> TokenStream { 190 | let mut andcondvec = Vec::::new(); 191 | 192 | // unpack the AndCondition and get all relations from within it 193 | for a in acond.0 { 194 | andcondvec.push(create_relation(a)); 195 | } 196 | 197 | // Unfold AndConditions and tokenize together with && 198 | quote! { ( #(#andcondvec)&&* ) } 199 | } 200 | 201 | // unfold OrConditions and tokenize together with || 202 | fn create_condition(cond: Condition) -> TokenStream { 203 | let mut condvec = Vec::::new(); 204 | 205 | // unpack the OrCondition and get all AndConditions from within it 206 | for c in cond.0 { 207 | condvec.push(create_and_condition(c)); 208 | } 209 | 210 | // unfold OrConditions and tokenize together with || 211 | quote! { #(#condvec)||* } 212 | } 213 | 214 | /// This Function takes a full condition as input and returns a TokenStream of the expression of the plural rule in Rust. 215 | pub fn gen_pr(cond: Condition) -> TokenStream { 216 | // create_condition(cond).into_token_stream() 217 | create_condition(cond) 218 | } 219 | -------------------------------------------------------------------------------- /make_pluralrules/src/parser/gen_rs.rs: -------------------------------------------------------------------------------- 1 | //! gen_rs is a Rust code generator for expression representations of CLDR plural rules. 2 | use super::plural_category::PluralCategory; 3 | use proc_macro2::{Literal, TokenStream}; 4 | use quote::quote; 5 | use std::collections::BTreeMap; 6 | use std::str; 7 | use unic_langid::LanguageIdentifier; 8 | 9 | /// Generates the complete TokenStream for the generated Rust code. This wraps the head and tail of the .rs file around the generated CLDR expressions. 10 | pub fn gen_fn(streams: BTreeMap>, vr: &str) -> TokenStream { 11 | let ignore_noncritical_errors = quote! { 12 | #![allow(unused_variables, unused_parens)] 13 | #![cfg_attr(feature = "cargo-clippy", allow(clippy::float_cmp))] 14 | #![cfg_attr(feature = "cargo-clippy", allow(clippy::unreadable_literal))] 15 | #![cfg_attr(feature = "cargo-clippy", allow(clippy::nonminimal_bool))] 16 | }; 17 | let use_statements = quote! { 18 | use super::operands::PluralOperands; 19 | use super::PluralCategory; 20 | use unic_langid::LanguageIdentifier; 21 | use unic_langid::subtags; 22 | }; 23 | let langid_macro = quote! { 24 | macro_rules! langid { 25 | ($lang:expr, $script:expr, $region:expr) => { 26 | { 27 | unsafe { 28 | LanguageIdentifier::from_raw_parts_unchecked( 29 | $lang, 30 | $script, 31 | $region, 32 | None, 33 | ) 34 | } 35 | } 36 | }; 37 | } 38 | }; 39 | let plural_function = quote! { pub type PluralRule = fn(&PluralOperands) -> PluralCategory; }; 40 | let num: isize = vr.parse().unwrap(); 41 | let ver = Literal::u64_unsuffixed(num as u64); 42 | let version = quote! { pub static CLDR_VERSION: usize = #ver; }; 43 | let head = quote! { #ignore_noncritical_errors #use_statements #plural_function #version #langid_macro }; 44 | let mut tokens = Vec::::new(); 45 | for (pr_type, stream) in streams { 46 | tokens.push(create_pr_type(&pr_type, stream)); 47 | } 48 | let prs = quote! { #(#tokens)* }; 49 | quote! { #head #prs } 50 | } 51 | 52 | // Function wraps all match statements for plural rules in a match for ordinal and cardinal rules 53 | fn create_pr_type(pr_type: &str, streams: Vec) -> TokenStream { 54 | let mut tokens = Vec::::new(); 55 | 56 | let match_name = match pr_type { 57 | "cardinal" => quote! { PRS_CARDINAL }, 58 | "ordinal" => quote! { PRS_ORDINAL }, 59 | _ => panic!("Unknown plural rule type"), 60 | }; 61 | 62 | for func in &streams { 63 | tokens.push(func.clone()); 64 | } 65 | quote! { pub const #match_name: &[(LanguageIdentifier, PluralRule)] = &[ #(#tokens),* ]; } 66 | } 67 | 68 | // Function wraps an expression in a match statement for plural category 69 | fn create_return(cat: PluralCategory, exp: &TokenStream) -> TokenStream { 70 | match cat { 71 | PluralCategory::ZERO => quote! {if #exp { PluralCategory::ZERO } }, 72 | PluralCategory::ONE => quote! {if #exp { PluralCategory::ONE } }, 73 | PluralCategory::TWO => quote! {if #exp { PluralCategory::TWO } }, 74 | PluralCategory::FEW => quote! {if #exp { PluralCategory::FEW } }, 75 | PluralCategory::MANY => quote! {if #exp { PluralCategory::MANY } }, 76 | PluralCategory::OTHER => quote! { { PluralCategory::OTHER } }, 77 | } 78 | } 79 | 80 | pub fn gen_langid(id: &LanguageIdentifier) -> TokenStream { 81 | let (lang, script, region, _) = id.clone().into_raw_parts(); 82 | let lang = if let Some(lang) = lang { 83 | quote!(subtags::Language::from_raw_unchecked(#lang)) 84 | } else { 85 | quote!(None) 86 | }; 87 | let script = if let Some(script) = script { 88 | quote!(Some(subtags::Script::from_raw_unchecked(#script))) 89 | } else { 90 | quote!(None) 91 | }; 92 | let region = if let Some(region) = region { 93 | quote!(Some(subtags::Region::from_raw_unchecked(#region))) 94 | } else { 95 | quote!(None) 96 | }; 97 | 98 | // No support for variants yet 99 | 100 | quote! { 101 | langid!( 102 | #lang, 103 | #script, 104 | #region 105 | ) 106 | } 107 | } 108 | 109 | /// Generates the closures that comprise the majority of the generated rust code. 110 | /// 111 | /// These statements are the expression representations of the CLDR plural rules. 112 | pub fn gen_mid( 113 | lang: &LanguageIdentifier, 114 | pluralrule_set: &[(PluralCategory, TokenStream)], 115 | ) -> TokenStream { 116 | let langid = gen_langid(lang); 117 | // make pluralrule_set iterable 118 | let mut iter = pluralrule_set.iter(); 119 | 120 | let queued = iter.next(); 121 | let rule_tokens = match queued { 122 | Some(pair) => { 123 | // instantiate tokenstream for folded match rules 124 | let mut tokens = create_return(pair.0, &pair.1); 125 | 126 | // add all tokens to token stream, separated by commas 127 | for pair in iter { 128 | let condition = create_return(pair.0, &pair.1); 129 | tokens = quote! { #tokens else #condition }; 130 | } 131 | tokens = quote! { #tokens else { PluralCategory::OTHER } }; 132 | tokens 133 | } 134 | None => quote! { { PluralCategory::OTHER } }, 135 | }; 136 | 137 | // We can't use a closure here because closures can't get rvalue 138 | // promoted to statics. They may in the future. 139 | quote! {( 140 | #langid, 141 | |po| { 142 | #rule_tokens 143 | } 144 | )} 145 | } 146 | -------------------------------------------------------------------------------- /make_pluralrules/src/parser/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod gen_pr; 2 | pub mod gen_rs; 3 | pub mod plural_category; 4 | pub mod resource; 5 | -------------------------------------------------------------------------------- /make_pluralrules/src/parser/plural_category.rs: -------------------------------------------------------------------------------- 1 | //! Plural rule categories in compliance with [Unicode](https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules) 2 | 3 | /// An enum for all plural rule categories. 4 | #[derive(Clone, Copy, Debug, PartialEq)] 5 | pub enum PluralCategory { 6 | ZERO, 7 | ONE, 8 | TWO, 9 | FEW, 10 | MANY, 11 | OTHER, 12 | } 13 | -------------------------------------------------------------------------------- /make_pluralrules/src/parser/resource.rs: -------------------------------------------------------------------------------- 1 | //! This reference parser uses serde_json to produce the plural rules from a CLDR data JSON file. 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use std::collections::BTreeMap; 6 | use std::error::Error; 7 | 8 | #[derive(Serialize, Deserialize, Debug)] 9 | pub struct Resource { 10 | pub supplemental: Supplemental, 11 | } 12 | 13 | #[derive(Serialize, Deserialize, Debug)] 14 | pub struct Version { 15 | #[serde(rename = "_number")] 16 | pub number: Option, 17 | #[serde(rename = "_unicodeVersion")] 18 | pub unicode_version: String, 19 | #[serde(rename = "_cldrVersion")] 20 | pub cldr_version: String, 21 | } 22 | 23 | #[derive(Serialize, Deserialize, Debug)] 24 | pub struct Supplemental { 25 | pub version: Version, 26 | #[serde(rename = "plurals-type-cardinal")] 27 | pub plurals_type_cardinal: Option>>, 28 | #[serde(rename = "plurals-type-ordinal")] 29 | pub plurals_type_ordinal: Option>>, 30 | } 31 | 32 | /// Will parse a CLDR compliant source from a &str. 33 | pub fn parse_plurals_resource_from_string(body: &str) -> Result> { 34 | let u = serde_json::from_str(body)?; 35 | Ok(u) 36 | } 37 | -------------------------------------------------------------------------------- /make_pluralrules/tests/fixtures/cldr_pluralrules_ordinals_33.json: -------------------------------------------------------------------------------- 1 | { 2 | "supplemental": { 3 | "version": { 4 | "_number": "$Revision: 13819 $", 5 | "_unicodeVersion": "10.0.0", 6 | "_cldrVersion": "33" 7 | }, 8 | "plurals-type-ordinal": { 9 | "af": { 10 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 11 | }, 12 | "am": { 13 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 14 | }, 15 | "ar": { 16 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 17 | }, 18 | "as": { 19 | "pluralRule-count-one": "n = 1,5,7,8,9,10 @integer 1, 5, 7~10", 20 | "pluralRule-count-two": "n = 2,3 @integer 2, 3", 21 | "pluralRule-count-few": "n = 4 @integer 4", 22 | "pluralRule-count-many": "n = 6 @integer 6", 23 | "pluralRule-count-other": " @integer 0, 11~25, 100, 1000, 10000, 100000, 1000000, …" 24 | }, 25 | "az": { 26 | "pluralRule-count-one": "i % 10 = 1,2,5,7,8 or i % 100 = 20,50,70,80 @integer 1, 2, 5, 7, 8, 11, 12, 15, 17, 18, 20~22, 25, 101, 1001, …", 27 | "pluralRule-count-few": "i % 10 = 3,4 or i % 1000 = 100,200,300,400,500,600,700,800,900 @integer 3, 4, 13, 14, 23, 24, 33, 34, 43, 44, 53, 54, 63, 64, 73, 74, 100, 1003, …", 28 | "pluralRule-count-many": "i = 0 or i % 10 = 6 or i % 100 = 40,60,90 @integer 0, 6, 16, 26, 36, 40, 46, 56, 106, 1006, …", 29 | "pluralRule-count-other": " @integer 9, 10, 19, 29, 30, 39, 49, 59, 69, 79, 109, 1000, 10000, 100000, 1000000, …" 30 | }, 31 | "be": { 32 | "pluralRule-count-few": "n % 10 = 2,3 and n % 100 != 12,13 @integer 2, 3, 22, 23, 32, 33, 42, 43, 52, 53, 62, 63, 72, 73, 82, 83, 102, 1002, …", 33 | "pluralRule-count-other": " @integer 0, 1, 4~17, 100, 1000, 10000, 100000, 1000000, …" 34 | }, 35 | "bg": { 36 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 37 | }, 38 | "bn": { 39 | "pluralRule-count-one": "n = 1,5,7,8,9,10 @integer 1, 5, 7~10", 40 | "pluralRule-count-two": "n = 2,3 @integer 2, 3", 41 | "pluralRule-count-few": "n = 4 @integer 4", 42 | "pluralRule-count-many": "n = 6 @integer 6", 43 | "pluralRule-count-other": " @integer 0, 11~25, 100, 1000, 10000, 100000, 1000000, …" 44 | }, 45 | "bs": { 46 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 47 | }, 48 | "ca": { 49 | "pluralRule-count-one": "n = 1,3 @integer 1, 3", 50 | "pluralRule-count-two": "n = 2 @integer 2", 51 | "pluralRule-count-few": "n = 4 @integer 4", 52 | "pluralRule-count-other": " @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …" 53 | }, 54 | "ce": { 55 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 56 | }, 57 | "cs": { 58 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 59 | }, 60 | "cy": { 61 | "pluralRule-count-zero": "n = 0,7,8,9 @integer 0, 7~9", 62 | "pluralRule-count-one": "n = 1 @integer 1", 63 | "pluralRule-count-two": "n = 2 @integer 2", 64 | "pluralRule-count-few": "n = 3,4 @integer 3, 4", 65 | "pluralRule-count-many": "n = 5,6 @integer 5, 6", 66 | "pluralRule-count-other": " @integer 10~25, 100, 1000, 10000, 100000, 1000000, …" 67 | }, 68 | "da": { 69 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 70 | }, 71 | "de": { 72 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 73 | }, 74 | "dsb": { 75 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 76 | }, 77 | "el": { 78 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 79 | }, 80 | "en": { 81 | "pluralRule-count-one": "n % 10 = 1 and n % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …", 82 | "pluralRule-count-two": "n % 10 = 2 and n % 100 != 12 @integer 2, 22, 32, 42, 52, 62, 72, 82, 102, 1002, …", 83 | "pluralRule-count-few": "n % 10 = 3 and n % 100 != 13 @integer 3, 23, 33, 43, 53, 63, 73, 83, 103, 1003, …", 84 | "pluralRule-count-other": " @integer 0, 4~18, 100, 1000, 10000, 100000, 1000000, …" 85 | }, 86 | "es": { 87 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 88 | }, 89 | "et": { 90 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 91 | }, 92 | "eu": { 93 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 94 | }, 95 | "fa": { 96 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 97 | }, 98 | "fi": { 99 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 100 | }, 101 | "fil": { 102 | "pluralRule-count-one": "n = 1 @integer 1", 103 | "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 104 | }, 105 | "fr": { 106 | "pluralRule-count-one": "n = 1 @integer 1", 107 | "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 108 | }, 109 | "fy": { 110 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 111 | }, 112 | "ga": { 113 | "pluralRule-count-one": "n = 1 @integer 1", 114 | "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 115 | }, 116 | "gl": { 117 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 118 | }, 119 | "gsw": { 120 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 121 | }, 122 | "gu": { 123 | "pluralRule-count-one": "n = 1 @integer 1", 124 | "pluralRule-count-two": "n = 2,3 @integer 2, 3", 125 | "pluralRule-count-few": "n = 4 @integer 4", 126 | "pluralRule-count-many": "n = 6 @integer 6", 127 | "pluralRule-count-other": " @integer 0, 5, 7~20, 100, 1000, 10000, 100000, 1000000, …" 128 | }, 129 | "he": { 130 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 131 | }, 132 | "hi": { 133 | "pluralRule-count-one": "n = 1 @integer 1", 134 | "pluralRule-count-two": "n = 2,3 @integer 2, 3", 135 | "pluralRule-count-few": "n = 4 @integer 4", 136 | "pluralRule-count-many": "n = 6 @integer 6", 137 | "pluralRule-count-other": " @integer 0, 5, 7~20, 100, 1000, 10000, 100000, 1000000, …" 138 | }, 139 | "hr": { 140 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 141 | }, 142 | "hsb": { 143 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 144 | }, 145 | "hu": { 146 | "pluralRule-count-one": "n = 1,5 @integer 1, 5", 147 | "pluralRule-count-other": " @integer 0, 2~4, 6~17, 100, 1000, 10000, 100000, 1000000, …" 148 | }, 149 | "hy": { 150 | "pluralRule-count-one": "n = 1 @integer 1", 151 | "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 152 | }, 153 | "id": { 154 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 155 | }, 156 | "in": { 157 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 158 | }, 159 | "is": { 160 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 161 | }, 162 | "it": { 163 | "pluralRule-count-many": "n = 11,8,80,800 @integer 8, 11, 80, 800", 164 | "pluralRule-count-other": " @integer 0~7, 9, 10, 12~17, 100, 1000, 10000, 100000, 1000000, …" 165 | }, 166 | "iw": { 167 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 168 | }, 169 | "ja": { 170 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 171 | }, 172 | "ka": { 173 | "pluralRule-count-one": "i = 1 @integer 1", 174 | "pluralRule-count-many": "i = 0 or i % 100 = 2..20,40,60,80 @integer 0, 2~16, 102, 1002, …", 175 | "pluralRule-count-other": " @integer 21~36, 100, 1000, 10000, 100000, 1000000, …" 176 | }, 177 | "kk": { 178 | "pluralRule-count-many": "n % 10 = 6 or n % 10 = 9 or n % 10 = 0 and n != 0 @integer 6, 9, 10, 16, 19, 20, 26, 29, 30, 36, 39, 40, 100, 1000, 10000, 100000, 1000000, …", 179 | "pluralRule-count-other": " @integer 0~5, 7, 8, 11~15, 17, 18, 21, 101, 1001, …" 180 | }, 181 | "km": { 182 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 183 | }, 184 | "kn": { 185 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 186 | }, 187 | "ko": { 188 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 189 | }, 190 | "ky": { 191 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 192 | }, 193 | "lo": { 194 | "pluralRule-count-one": "n = 1 @integer 1", 195 | "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 196 | }, 197 | "lt": { 198 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 199 | }, 200 | "lv": { 201 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 202 | }, 203 | "mk": { 204 | "pluralRule-count-one": "i % 10 = 1 and i % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …", 205 | "pluralRule-count-two": "i % 10 = 2 and i % 100 != 12 @integer 2, 22, 32, 42, 52, 62, 72, 82, 102, 1002, …", 206 | "pluralRule-count-many": "i % 10 = 7,8 and i % 100 != 17,18 @integer 7, 8, 27, 28, 37, 38, 47, 48, 57, 58, 67, 68, 77, 78, 87, 88, 107, 1007, …", 207 | "pluralRule-count-other": " @integer 0, 3~6, 9~19, 100, 1000, 10000, 100000, 1000000, …" 208 | }, 209 | "ml": { 210 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 211 | }, 212 | "mn": { 213 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 214 | }, 215 | "mo": { 216 | "pluralRule-count-one": "n = 1 @integer 1", 217 | "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 218 | }, 219 | "mr": { 220 | "pluralRule-count-one": "n = 1 @integer 1", 221 | "pluralRule-count-two": "n = 2,3 @integer 2, 3", 222 | "pluralRule-count-few": "n = 4 @integer 4", 223 | "pluralRule-count-other": " @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …" 224 | }, 225 | "ms": { 226 | "pluralRule-count-one": "n = 1 @integer 1", 227 | "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 228 | }, 229 | "my": { 230 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 231 | }, 232 | "nb": { 233 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 234 | }, 235 | "ne": { 236 | "pluralRule-count-one": "n = 1..4 @integer 1~4", 237 | "pluralRule-count-other": " @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …" 238 | }, 239 | "nl": { 240 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 241 | }, 242 | "or": { 243 | "pluralRule-count-one": "n = 1,5,7..9 @integer 1, 5, 7~9", 244 | "pluralRule-count-two": "n = 2,3 @integer 2, 3", 245 | "pluralRule-count-few": "n = 4 @integer 4", 246 | "pluralRule-count-many": "n = 6 @integer 6", 247 | "pluralRule-count-other": " @integer 0, 10~24, 100, 1000, 10000, 100000, 1000000, …" 248 | }, 249 | "pa": { 250 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 251 | }, 252 | "pl": { 253 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 254 | }, 255 | "prg": { 256 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 257 | }, 258 | "ps": { 259 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 260 | }, 261 | "pt": { 262 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 263 | }, 264 | "ro": { 265 | "pluralRule-count-one": "n = 1 @integer 1", 266 | "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 267 | }, 268 | "root": { 269 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 270 | }, 271 | "ru": { 272 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 273 | }, 274 | "scn": { 275 | "pluralRule-count-many": "n = 11,8,80,800 @integer 8, 11, 80, 800", 276 | "pluralRule-count-other": " @integer 0~7, 9, 10, 12~17, 100, 1000, 10000, 100000, 1000000, …" 277 | }, 278 | "sd": { 279 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 280 | }, 281 | "sh": { 282 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 283 | }, 284 | "si": { 285 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 286 | }, 287 | "sk": { 288 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 289 | }, 290 | "sl": { 291 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 292 | }, 293 | "sq": { 294 | "pluralRule-count-one": "n = 1 @integer 1", 295 | "pluralRule-count-many": "n % 10 = 4 and n % 100 != 14 @integer 4, 24, 34, 44, 54, 64, 74, 84, 104, 1004, …", 296 | "pluralRule-count-other": " @integer 0, 2, 3, 5~17, 100, 1000, 10000, 100000, 1000000, …" 297 | }, 298 | "sr": { 299 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 300 | }, 301 | "sv": { 302 | "pluralRule-count-one": "n % 10 = 1,2 and n % 100 != 11,12 @integer 1, 2, 21, 22, 31, 32, 41, 42, 51, 52, 61, 62, 71, 72, 81, 82, 101, 1001, …", 303 | "pluralRule-count-other": " @integer 0, 3~17, 100, 1000, 10000, 100000, 1000000, …" 304 | }, 305 | "sw": { 306 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 307 | }, 308 | "ta": { 309 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 310 | }, 311 | "te": { 312 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 313 | }, 314 | "th": { 315 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 316 | }, 317 | "tk": { 318 | "pluralRule-count-few": "n % 10 = 6,9 or n = 10 @integer 6, 9, 10, 16, 19, 26, 29, 36, 39, 106, 1006, …", 319 | "pluralRule-count-other": " @integer 0~5, 7, 8, 11~15, 17, 18, 20, 100, 1000, 10000, 100000, 1000000, …" 320 | }, 321 | "tl": { 322 | "pluralRule-count-one": "n = 1 @integer 1", 323 | "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 324 | }, 325 | "tr": { 326 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 327 | }, 328 | "uk": { 329 | "pluralRule-count-few": "n % 10 = 3 and n % 100 != 13 @integer 3, 23, 33, 43, 53, 63, 73, 83, 103, 1003, …", 330 | "pluralRule-count-other": " @integer 0~2, 4~16, 100, 1000, 10000, 100000, 1000000, …" 331 | }, 332 | "ur": { 333 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 334 | }, 335 | "uz": { 336 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 337 | }, 338 | "vi": { 339 | "pluralRule-count-one": "n = 1 @integer 1", 340 | "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 341 | }, 342 | "yue": { 343 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 344 | }, 345 | "zh": { 346 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 347 | }, 348 | "zu": { 349 | "pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, …" 350 | } 351 | } 352 | } 353 | } 354 | -------------------------------------------------------------------------------- /make_pluralrules/tests/fixtures/cldr_pluralrules_within_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "supplemental": { 3 | "version": { 4 | "_number": "$Revision: 13898 $", 5 | "_unicodeVersion": "10.0.0", 6 | "_cldrVersion": "0" 7 | }, 8 | "plurals-type-cardinal": { 9 | "und": { 10 | "pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000", 11 | "pluralRule-count-two": "n % 10 within 1..2 @ 11, 12, 21, 22...", 12 | "pluralRule-count-few": "n within 2..10 and i not within 8..9 @ 1.1, 1.2, 2, 3, 4, 5, 6, 7, 10", 13 | "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …" 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /make_pluralrules/tests/fixtures/cldr_pluralrules_within_test.rs: -------------------------------------------------------------------------------- 1 | # ! [allow (unused_variables , unused_parens)] # ! [cfg_attr (feature = "cargo-clippy" , allow (clippy :: float_cmp))] # ! [cfg_attr (feature = "cargo-clippy" , allow (clippy :: unreadable_literal))] # ! [cfg_attr (feature = "cargo-clippy" , allow (clippy :: nonminimal_bool))] use super :: operands :: PluralOperands ; use super :: PluralCategory ; use unic_langid :: LanguageIdentifier ; use unic_langid :: subtags ; pub type PluralRule = fn (& PluralOperands) -> PluralCategory ; pub static CLDR_VERSION : usize = 0 ; macro_rules ! langid { ($ lang : expr , $ script : expr , $ region : expr) => { { unsafe { LanguageIdentifier :: from_raw_parts_unchecked ($ lang , $ script , $ region , None ,) } } } ; } pub const PRS_CARDINAL : & [(LanguageIdentifier , PluralRule)] = & [(langid ! (None , None , None) , | po | { if (2 . 0 <= po . n && po . n <= 10 . 0 && 8 > po . i && po . i > 9) { PluralCategory :: FEW } else if (po . n == 1 . 0) { PluralCategory :: ONE } else if (1 <= po . i % 10 && po . i % 10 <= 2) { PluralCategory :: TWO } else { PluralCategory :: OTHER } })] ; -------------------------------------------------------------------------------- /make_pluralrules/tests/test.rs: -------------------------------------------------------------------------------- 1 | use make_pluralrules::generate_rs; 2 | 3 | use std::fs::File; 4 | use std::io; 5 | use std::io::Read; 6 | 7 | fn read_file(path: &str) -> Result { 8 | let mut f = File::open(path)?; 9 | let mut s = String::new(); 10 | f.read_to_string(&mut s)?; 11 | Ok(s.trim().to_string()) 12 | } 13 | 14 | #[test] 15 | fn full_cldr_test() { 16 | let cardinal_json = read_file("./tests/fixtures/cldr_pluralrules_cardinals_33.json") 17 | .expect("Could not read input json"); 18 | let ordinal_json = read_file("./tests/fixtures/cldr_pluralrules_ordinals_33.json") 19 | .expect("Could not read input json"); 20 | let output_rs = 21 | read_file("./tests/fixtures/cldr_pluralrules_33.rs").expect("Could not read output rs"); 22 | 23 | let output = generate_rs(&[cardinal_json, ordinal_json]); 24 | 25 | assert_eq!(output_rs, output); 26 | } 27 | 28 | #[test] 29 | fn within_test() { 30 | let input_json = read_file("./tests/fixtures/cldr_pluralrules_within_test.json") 31 | .expect("Could not read input json"); 32 | let output_rs = read_file("./tests/fixtures/cldr_pluralrules_within_test.rs") 33 | .expect("Could not read output rs"); 34 | 35 | let output = generate_rs(&[input_json]); 36 | 37 | assert_eq!(output_rs, output); 38 | } 39 | 40 | #[test] 41 | #[should_panic] 42 | fn bad_type_test() { 43 | let text = String::from( 44 | r#"{ 45 | "supplemental": { 46 | "version": { 47 | "_number": "$Revision: 13898 $", 48 | "_unicodeVersion": "10.0.0", 49 | "_cldrVersion": "0" 50 | }, 51 | "plurals-type-cardinals": { 52 | "test": { 53 | "pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000", 54 | "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …" 55 | } 56 | } 57 | } 58 | }"#, 59 | ); 60 | 61 | generate_rs(&[text]); 62 | } 63 | 64 | #[test] 65 | #[should_panic] 66 | fn same_data_cardinal_test() { 67 | let cardinal_json = read_file("./tests/fixtures/cldr_pluralrules_cardinals_33.json") 68 | .expect("Could not read input json"); 69 | let copy_json = read_file("./tests/fixtures/cldr_pluralrules_cardinals_33.json") 70 | .expect("Could not read input json"); 71 | 72 | generate_rs(&[cardinal_json, copy_json]); 73 | } 74 | 75 | #[test] 76 | #[should_panic] 77 | fn same_data_ordinal_test() { 78 | let cardinal_json = read_file("./tests/fixtures/cldr_pluralrules_ordinals_33.json") 79 | .expect("Could not read input json"); 80 | let copy_json = read_file("./tests/fixtures/cldr_pluralrules_ordinals_33.json") 81 | .expect("Could not read input json"); 82 | 83 | generate_rs(&[cardinal_json, copy_json]); 84 | } 85 | 86 | #[test] 87 | #[should_panic] 88 | fn different_version_test() { 89 | let cardinal_json = String::from( 90 | r#"{ 91 | "supplemental": { 92 | "version": { 93 | "_number": "$Revision: 13898 $", 94 | "_unicodeVersion": "10.0.0", 95 | "_cldrVersion": "0" 96 | }, 97 | "plurals-type-cardinals": { 98 | "test": { 99 | "pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000", 100 | "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …" 101 | } 102 | } 103 | } 104 | }"#, 105 | ); 106 | let ordinal_json = String::from( 107 | r#"{ 108 | "supplemental": { 109 | "version": { 110 | "_number": "$Revision: 13898 $", 111 | "_unicodeVersion": "10.0.0", 112 | "_cldrVersion": "1" 113 | }, 114 | "plurals-type-cardinals": { 115 | "test": { 116 | "pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000", 117 | "pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …" 118 | } 119 | } 120 | } 121 | }"#, 122 | ); 123 | 124 | generate_rs(&[cardinal_json, ordinal_json]); 125 | } 126 | --------------------------------------------------------------------------------