/rust/registry/src/index.crates.io-1949cf8c6b5b557f/icu_plurals-1.5.0/src/lib.rs
Line | Count | Source |
1 | | // This file is part of ICU4X. For terms of use, please see the file |
2 | | // called LICENSE at the top level of the ICU4X source tree |
3 | | // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). |
4 | | |
5 | | //! Determine the plural category appropriate for a given number in a given language. |
6 | | //! |
7 | | //! This module is published as its own crate ([`icu_plurals`](https://docs.rs/icu_plurals/latest/icu_plurals/)) |
8 | | //! and as part of the [`icu`](https://docs.rs/icu/latest/icu/) crate. See the latter for more details on the ICU4X project. |
9 | | //! |
10 | | //! For example in English, when constructing a message |
11 | | //! such as `{ num } items`, the user has to prepare |
12 | | //! two variants of the message: |
13 | | //! |
14 | | //! * `1 item` |
15 | | //! * `0 items`, `2 items`, `5 items`, `0.5 items` etc. |
16 | | //! |
17 | | //! The former variant is used when the placeholder variable has value `1`, |
18 | | //! while the latter is used for all other values of the variable. |
19 | | //! |
20 | | //! Unicode defines [Language Plural Rules] as a mechanism to codify those |
21 | | //! variants and provides data and algorithms to calculate |
22 | | //! appropriate [`PluralCategory`]. |
23 | | //! |
24 | | //! # Examples |
25 | | //! |
26 | | //! ``` |
27 | | //! use icu::locid::locale; |
28 | | //! use icu::plurals::{PluralCategory, PluralRuleType, PluralRules}; |
29 | | //! |
30 | | //! let pr = |
31 | | //! PluralRules::try_new(&locale!("en").into(), PluralRuleType::Cardinal) |
32 | | //! .expect("locale should be present"); |
33 | | //! |
34 | | //! assert_eq!(pr.category_for(5_usize), PluralCategory::Other); |
35 | | //! ``` |
36 | | //! |
37 | | //! ## Plural Rules |
38 | | //! |
39 | | //! The crate provides the main struct [`PluralRules`] which handles selection |
40 | | //! of the correct [`PluralCategory`] for a given language and [`PluralRuleType`]. |
41 | | //! |
42 | | //! ## Plural Category |
43 | | //! |
44 | | //! Every number in every language belongs to a certain [`PluralCategory`]. |
45 | | //! For example, the Polish language uses four: |
46 | | //! |
47 | | //! * [`One`](PluralCategory::One): `1 miesiąc` |
48 | | //! * [`Few`](PluralCategory::Few): `2 miesiące` |
49 | | //! * [`Many`](PluralCategory::Many): `5 miesięcy` |
50 | | //! * [`Other`](PluralCategory::Other): `1.5 miesiąca` |
51 | | //! |
52 | | //! ## `PluralRuleType` |
53 | | //! |
54 | | //! Plural rules depend on the use case. This crate supports two types of plural rules: |
55 | | //! |
56 | | //! * [`Cardinal`](PluralRuleType::Cardinal): `3 doors`, `1 month`, `10 dollars` |
57 | | //! * [`Ordinal`](PluralRuleType::Ordinal): `1st place`, `10th day`, `11th floor` |
58 | | //! |
59 | | //! [Language Plural Rules]: https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules |
60 | | |
61 | | // https://github.com/unicode-org/icu4x/blob/main/documents/process/boilerplate.md#library-annotations |
62 | | #![cfg_attr(not(any(test, feature = "std")), no_std)] |
63 | | #![cfg_attr( |
64 | | not(test), |
65 | | deny( |
66 | | clippy::indexing_slicing, |
67 | | clippy::unwrap_used, |
68 | | clippy::expect_used, |
69 | | clippy::panic, |
70 | | clippy::exhaustive_structs, |
71 | | clippy::exhaustive_enums, |
72 | | missing_debug_implementations, |
73 | | ) |
74 | | )] |
75 | | #![warn(missing_docs)] |
76 | | |
77 | | extern crate alloc; |
78 | | |
79 | | mod error; |
80 | | mod operands; |
81 | | pub mod provider; |
82 | | pub mod rules; |
83 | | |
84 | | use core::cmp::{Ord, PartialOrd}; |
85 | | pub use error::PluralsError; |
86 | | use icu_provider::prelude::*; |
87 | | pub use operands::PluralOperands; |
88 | | use provider::CardinalV1Marker; |
89 | | use provider::ErasedPluralRulesV1Marker; |
90 | | use provider::OrdinalV1Marker; |
91 | | use rules::runtime::test_rule; |
92 | | |
93 | | #[cfg(feature = "experimental")] |
94 | | use provider::PluralRangesV1Marker; |
95 | | #[cfg(feature = "experimental")] |
96 | | use provider::UnvalidatedPluralRange; |
97 | | |
98 | | #[doc(no_inline)] |
99 | | pub use PluralsError as Error; |
100 | | |
101 | | /// A type of a plural rule which can be associated with the [`PluralRules`] struct. |
102 | | #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] |
103 | | #[non_exhaustive] |
104 | | pub enum PluralRuleType { |
105 | | /// Cardinal plural forms express quantities of units such as time, currency or distance, |
106 | | /// used in conjunction with a number expressed in decimal digits (i.e. "2", not "two"). |
107 | | /// |
108 | | /// For example, English has two forms for cardinals: |
109 | | /// |
110 | | /// * [`One`]: `1 day` |
111 | | /// * [`Other`]: `0 days`, `2 days`, `10 days`, `0.3 days` |
112 | | /// |
113 | | /// [`One`]: PluralCategory::One |
114 | | /// [`Other`]: PluralCategory::Other |
115 | | Cardinal, |
116 | | /// Ordinal plural forms denote the order of items in a set and are always integers. |
117 | | /// |
118 | | /// For example, English has four forms for ordinals: |
119 | | /// |
120 | | /// * [`One`]: `1st floor`, `21st floor`, `101st floor` |
121 | | /// * [`Two`]: `2nd floor`, `22nd floor`, `102nd floor` |
122 | | /// * [`Few`]: `3rd floor`, `23rd floor`, `103rd floor` |
123 | | /// * [`Other`]: `4th floor`, `11th floor`, `96th floor` |
124 | | /// |
125 | | /// [`One`]: PluralCategory::One |
126 | | /// [`Two`]: PluralCategory::Two |
127 | | /// [`Few`]: PluralCategory::Few |
128 | | /// [`Other`]: PluralCategory::Other |
129 | | Ordinal, |
130 | | } |
131 | | |
132 | | /// The plural categories are used to format messages with numeric placeholders, expressed as decimal numbers. |
133 | | /// |
134 | | /// The fundamental rule for determining plural categories is the existence of minimal pairs: whenever two different |
135 | | /// numbers may require different versions of the same message, then the numbers have different plural categories. |
136 | | /// |
137 | | /// All languages supported by `ICU4X` can match any number to one of the categories. |
138 | | /// |
139 | | /// # Examples |
140 | | /// |
141 | | /// ``` |
142 | | /// use icu::locid::locale; |
143 | | /// use icu::plurals::{PluralCategory, PluralRuleType, PluralRules}; |
144 | | /// |
145 | | /// let pr = |
146 | | /// PluralRules::try_new(&locale!("en").into(), PluralRuleType::Cardinal) |
147 | | /// .expect("locale should be present"); |
148 | | /// |
149 | | /// assert_eq!(pr.category_for(5_usize), PluralCategory::Other); |
150 | | /// ``` |
151 | | #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Ord, PartialOrd)] |
152 | | #[cfg_attr(feature = "datagen", derive(serde::Serialize))] |
153 | | #[cfg_attr(feature = "serde", derive(serde::Deserialize))] |
154 | | #[repr(u8)] |
155 | | #[zerovec::make_ule(PluralCategoryULE)] |
156 | | #[allow(clippy::exhaustive_enums)] // this type is mostly stable. new categories may potentially be added in the future, |
157 | | // but at a cadence slower than the ICU4X release cycle |
158 | | pub enum PluralCategory { |
159 | | /// CLDR "zero" plural category. Used in Arabic and Latvian, among others. |
160 | | /// |
161 | | /// Examples of numbers having this category: |
162 | | /// |
163 | | /// - 0 in Arabic (ar), Latvian (lv) |
164 | | /// - 10~20, 30, 40, 50, ... in Latvian (lv) |
165 | | Zero = 0, |
166 | | /// CLDR "one" plural category. Signifies the singular form in many languages. |
167 | | /// |
168 | | /// Examples of numbers having this category: |
169 | | /// |
170 | | /// - 0 in French (fr), Portuguese (pt), ... |
171 | | /// - 1 in English (en) and most other languages |
172 | | /// - 2.1 in Filipino (fil), Croatian (hr), Latvian (lv), Serbian (sr) |
173 | | /// - 2, 3, 5, 7, 8, ... in Filipino (fil) |
174 | | One = 1, |
175 | | /// CLDR "two" plural category. Used in Arabic, Hebrew, and Slovenian, among others. |
176 | | /// |
177 | | /// Examples of numbers having this category: |
178 | | /// |
179 | | /// - 2 in Arabic (ar), Hebrew (iw), Slovenian (sl) |
180 | | /// - 2.0 in Arabic (ar) |
181 | | Two = 2, |
182 | | /// CLDR "few" plural category. Used in Romanian, Polish, Russian, and others. |
183 | | /// |
184 | | /// Examples of numbers having this category: |
185 | | /// |
186 | | /// - 0 in Romanian (ro) |
187 | | /// - 1.2 in Croatian (hr), Romanian (ro), Slovenian (sl), Serbian (sr) |
188 | | /// - 2 in Polish (pl), Russian (ru), Czech (cs), ... |
189 | | /// - 5 in Arabic (ar), Lithuanian (lt), Romanian (ro) |
190 | | Few = 3, |
191 | | /// CLDR "many" plural category. Used in Polish, Russian, Ukrainian, and others. |
192 | | /// |
193 | | /// Examples of numbers having this category: |
194 | | /// |
195 | | /// - 0 in Polish (pl) |
196 | | /// - 1.0 in Czech (cs), Slovak (sk) |
197 | | /// - 1.1 in Czech (cs), Lithuanian (lt), Slovak (sk) |
198 | | /// - 15 in Arabic (ar), Polish (pl), Russian (ru), Ukrainian (uk) |
199 | | Many = 4, |
200 | | /// CLDR "other" plural category, used as a catch-all. Each language supports it, and it |
201 | | /// is also used as a fail safe result for in case no better match can be identified. |
202 | | /// |
203 | | /// In some languages, such as Japanese, Chinese, Korean, and Thai, this is the only |
204 | | /// plural category. |
205 | | /// |
206 | | /// Examples of numbers having this category: |
207 | | /// |
208 | | /// - 0 in English (en), German (de), Spanish (es), ... |
209 | | /// - 1 in Japanese (ja), Korean (ko), Chinese (zh), Thai (th), ... |
210 | | /// - 2 in English (en), German (de), Spanish (es), ... |
211 | | Other = 5, |
212 | | } |
213 | | |
214 | | impl PluralCategory { |
215 | | /// Returns an ordered iterator over variants of [`Plural Categories`]. |
216 | | /// |
217 | | /// Categories are returned in alphabetical order. |
218 | | /// |
219 | | /// # Examples |
220 | | /// |
221 | | /// ``` |
222 | | /// use icu::plurals::PluralCategory; |
223 | | /// |
224 | | /// let mut categories = PluralCategory::all(); |
225 | | /// |
226 | | /// assert_eq!(categories.next(), Some(PluralCategory::Few)); |
227 | | /// assert_eq!(categories.next(), Some(PluralCategory::Many)); |
228 | | /// assert_eq!(categories.next(), Some(PluralCategory::One)); |
229 | | /// assert_eq!(categories.next(), Some(PluralCategory::Other)); |
230 | | /// assert_eq!(categories.next(), Some(PluralCategory::Two)); |
231 | | /// assert_eq!(categories.next(), Some(PluralCategory::Zero)); |
232 | | /// assert_eq!(categories.next(), None); |
233 | | /// ``` |
234 | | /// |
235 | | /// [`Plural Categories`]: PluralCategory |
236 | 0 | pub fn all() -> impl ExactSizeIterator<Item = Self> { |
237 | 0 | [ |
238 | 0 | Self::Few, |
239 | 0 | Self::Many, |
240 | 0 | Self::One, |
241 | 0 | Self::Other, |
242 | 0 | Self::Two, |
243 | 0 | Self::Zero, |
244 | 0 | ] |
245 | 0 | .iter() |
246 | 0 | .copied() |
247 | 0 | } |
248 | | |
249 | | /// Returns the PluralCategory corresponding to given TR35 string. |
250 | 0 | pub fn get_for_cldr_string(category: &str) -> Option<PluralCategory> { |
251 | 0 | Self::get_for_cldr_bytes(category.as_bytes()) |
252 | 0 | } |
253 | | /// Returns the PluralCategory corresponding to given TR35 string as bytes |
254 | 0 | pub fn get_for_cldr_bytes(category: &[u8]) -> Option<PluralCategory> { |
255 | 0 | match category { |
256 | 0 | b"zero" => Some(PluralCategory::Zero), |
257 | 0 | b"one" => Some(PluralCategory::One), |
258 | 0 | b"two" => Some(PluralCategory::Two), |
259 | 0 | b"few" => Some(PluralCategory::Few), |
260 | 0 | b"many" => Some(PluralCategory::Many), |
261 | 0 | b"other" => Some(PluralCategory::Other), |
262 | 0 | _ => None, |
263 | | } |
264 | 0 | } |
265 | | } |
266 | | |
267 | | /// A struct which provides an ability to retrieve an appropriate |
268 | | /// [`Plural Category`] for a given number. |
269 | | /// |
270 | | /// # Examples |
271 | | /// |
272 | | /// ``` |
273 | | /// use icu::locid::locale; |
274 | | /// use icu::plurals::{PluralCategory, PluralRuleType, PluralRules}; |
275 | | /// |
276 | | /// let pr = |
277 | | /// PluralRules::try_new(&locale!("en").into(), PluralRuleType::Cardinal) |
278 | | /// .expect("locale should be present"); |
279 | | /// |
280 | | /// assert_eq!(pr.category_for(5_usize), PluralCategory::Other); |
281 | | /// ``` |
282 | | /// |
283 | | /// [`ICU4X`]: ../icu/index.html |
284 | | /// [`Plural Type`]: PluralRuleType |
285 | | /// [`Plural Category`]: PluralCategory |
286 | | #[derive(Debug)] |
287 | | pub struct PluralRules(DataPayload<ErasedPluralRulesV1Marker>); |
288 | | |
289 | | impl AsRef<PluralRules> for PluralRules { |
290 | 0 | fn as_ref(&self) -> &PluralRules { |
291 | 0 | self |
292 | 0 | } |
293 | | } |
294 | | |
295 | | impl PluralRules { |
296 | | icu_provider::gen_any_buffer_data_constructors!( |
297 | | locale: include, |
298 | | rule_type: PluralRuleType, |
299 | | error: PluralsError, |
300 | | /// Constructs a new `PluralRules` for a given locale and type using compiled data. |
301 | | /// |
302 | | /// ✨ *Enabled with the `compiled_data` Cargo feature.* |
303 | | /// |
304 | | /// [📚 Help choosing a constructor](icu_provider::constructors) |
305 | | /// |
306 | | /// # Examples |
307 | | /// |
308 | | /// ``` |
309 | | /// use icu::locid::locale; |
310 | | /// use icu::plurals::{PluralRuleType, PluralRules}; |
311 | | /// |
312 | | /// let _ = PluralRules::try_new( |
313 | | /// &locale!("en").into(), |
314 | | /// PluralRuleType::Cardinal, |
315 | | /// ).expect("locale should be present"); |
316 | | /// ``` |
317 | | /// |
318 | | /// [`type`]: PluralRuleType |
319 | | /// [`data provider`]: icu_provider |
320 | | ); |
321 | | |
322 | | #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::try_new)] |
323 | 0 | pub fn try_new_unstable( |
324 | 0 | provider: &(impl DataProvider<CardinalV1Marker> + DataProvider<OrdinalV1Marker> + ?Sized), |
325 | 0 | locale: &DataLocale, |
326 | 0 | rule_type: PluralRuleType, |
327 | 0 | ) -> Result<Self, PluralsError> { |
328 | 0 | match rule_type { |
329 | 0 | PluralRuleType::Cardinal => Self::try_new_cardinal_unstable(provider, locale), |
330 | 0 | PluralRuleType::Ordinal => Self::try_new_ordinal_unstable(provider, locale), |
331 | | } |
332 | 0 | } |
333 | | |
334 | | icu_provider::gen_any_buffer_data_constructors!( |
335 | | locale: include, |
336 | | options: skip, |
337 | | error: PluralsError, |
338 | | /// Constructs a new `PluralRules` for a given locale for cardinal numbers using compiled data. |
339 | | /// |
340 | | /// Cardinal plural forms express quantities of units such as time, currency or distance, |
341 | | /// used in conjunction with a number expressed in decimal digits (i.e. "2", not "two"). |
342 | | /// |
343 | | /// For example, English has two forms for cardinals: |
344 | | /// |
345 | | /// * [`One`]: `1 day` |
346 | | /// * [`Other`]: `0 days`, `2 days`, `10 days`, `0.3 days` |
347 | | /// |
348 | | /// ✨ *Enabled with the `compiled_data` Cargo feature.* |
349 | | /// |
350 | | /// [📚 Help choosing a constructor](icu_provider::constructors) |
351 | | /// |
352 | | /// # Examples |
353 | | /// |
354 | | /// ``` |
355 | | /// use icu::locid::locale; |
356 | | /// use icu::plurals::{PluralCategory, PluralRules}; |
357 | | /// |
358 | | /// let rules = PluralRules::try_new_cardinal(&locale!("ru").into()).expect("locale should be present"); |
359 | | /// |
360 | | /// assert_eq!(rules.category_for(2_usize), PluralCategory::Few); |
361 | | /// ``` |
362 | | /// |
363 | | /// [`One`]: PluralCategory::One |
364 | | /// [`Other`]: PluralCategory::Other |
365 | | functions: [ |
366 | | try_new_cardinal, |
367 | | try_new_cardinal_with_any_provider, |
368 | | try_new_cardinal_with_buffer_provider, |
369 | | try_new_cardinal_unstable, |
370 | | Self, |
371 | | ] |
372 | | ); |
373 | | |
374 | | #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::try_new_cardinal)] |
375 | 0 | pub fn try_new_cardinal_unstable( |
376 | 0 | provider: &(impl DataProvider<CardinalV1Marker> + ?Sized), |
377 | 0 | locale: &DataLocale, |
378 | 0 | ) -> Result<Self, PluralsError> { |
379 | | Ok(Self( |
380 | 0 | provider |
381 | 0 | .load(DataRequest { |
382 | 0 | locale, |
383 | 0 | metadata: Default::default(), |
384 | 0 | })? |
385 | 0 | .take_payload()? |
386 | 0 | .cast(), |
387 | | )) |
388 | 0 | } Unexecuted instantiation: <icu_plurals::PluralRules>::try_new_cardinal_unstable::<icu_provider::any::DowncastingAnyProvider<icu_provider_adapters::empty::EmptyDataProvider>> Unexecuted instantiation: <icu_plurals::PluralRules>::try_new_cardinal_unstable::<icu_plurals::provider::Baked> |
389 | | |
390 | | icu_provider::gen_any_buffer_data_constructors!( |
391 | | locale: include, |
392 | | options: skip, |
393 | | error: PluralsError, |
394 | | /// Constructs a new `PluralRules` for a given locale for ordinal numbers using compiled data. |
395 | | /// |
396 | | /// Ordinal plural forms denote the order of items in a set and are always integers. |
397 | | /// |
398 | | /// For example, English has four forms for ordinals: |
399 | | /// |
400 | | /// * [`One`]: `1st floor`, `21st floor`, `101st floor` |
401 | | /// * [`Two`]: `2nd floor`, `22nd floor`, `102nd floor` |
402 | | /// * [`Few`]: `3rd floor`, `23rd floor`, `103rd floor` |
403 | | /// * [`Other`]: `4th floor`, `11th floor`, `96th floor` |
404 | | /// |
405 | | /// ✨ *Enabled with the `compiled_data` Cargo feature.* |
406 | | /// |
407 | | /// [📚 Help choosing a constructor](icu_provider::constructors) |
408 | | /// |
409 | | /// # Examples |
410 | | /// |
411 | | /// ``` |
412 | | /// use icu::locid::locale; |
413 | | /// use icu::plurals::{PluralCategory, PluralRules}; |
414 | | /// |
415 | | /// let rules = PluralRules::try_new_ordinal( |
416 | | /// &locale!("ru").into(), |
417 | | /// ) |
418 | | /// .expect("locale should be present"); |
419 | | /// |
420 | | /// assert_eq!(rules.category_for(2_usize), PluralCategory::Other); |
421 | | /// ``` |
422 | | /// |
423 | | /// [`One`]: PluralCategory::One |
424 | | /// [`Two`]: PluralCategory::Two |
425 | | /// [`Few`]: PluralCategory::Few |
426 | | /// [`Other`]: PluralCategory::Other |
427 | | functions: [ |
428 | | try_new_ordinal, |
429 | | try_new_ordinal_with_any_provider, |
430 | | try_new_ordinal_with_buffer_provider, |
431 | | try_new_ordinal_unstable, |
432 | | Self, |
433 | | ] |
434 | | ); |
435 | | |
436 | | #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::try_new_ordinal)] |
437 | 0 | pub fn try_new_ordinal_unstable( |
438 | 0 | provider: &(impl DataProvider<OrdinalV1Marker> + ?Sized), |
439 | 0 | locale: &DataLocale, |
440 | 0 | ) -> Result<Self, PluralsError> { |
441 | | Ok(Self( |
442 | 0 | provider |
443 | 0 | .load(DataRequest { |
444 | 0 | locale, |
445 | 0 | metadata: Default::default(), |
446 | 0 | })? |
447 | 0 | .take_payload()? |
448 | 0 | .cast(), |
449 | | )) |
450 | 0 | } Unexecuted instantiation: <icu_plurals::PluralRules>::try_new_ordinal_unstable::<icu_provider::any::DowncastingAnyProvider<icu_provider_adapters::empty::EmptyDataProvider>> Unexecuted instantiation: <icu_plurals::PluralRules>::try_new_ordinal_unstable::<icu_plurals::provider::Baked> |
451 | | |
452 | | /// Returns the [`Plural Category`] appropriate for the given number. |
453 | | /// |
454 | | /// # Examples |
455 | | /// |
456 | | /// ``` |
457 | | /// use icu::locid::locale; |
458 | | /// use icu::plurals::{PluralCategory, PluralRuleType, PluralRules}; |
459 | | /// |
460 | | /// let pr = |
461 | | /// PluralRules::try_new(&locale!("en").into(), PluralRuleType::Cardinal) |
462 | | /// .expect("locale should be present"); |
463 | | /// |
464 | | /// match pr.category_for(1_usize) { |
465 | | /// PluralCategory::One => "One item", |
466 | | /// PluralCategory::Other => "Many items", |
467 | | /// _ => unreachable!(), |
468 | | /// }; |
469 | | /// ``` |
470 | | /// |
471 | | /// The method accepts any input that can be calculated into [`Plural Operands`]. |
472 | | /// All unsigned primitive number types can infallibly be converted so they can be |
473 | | /// used as an input. |
474 | | /// |
475 | | /// For signed numbers and strings, [`Plural Operands`] implement [`TryFrom`] |
476 | | /// and [`FromStr`](std::str::FromStr), which should be used before passing the result to |
477 | | /// [`category_for()`](PluralRules::category_for()). |
478 | | /// |
479 | | /// # Examples |
480 | | /// |
481 | | /// ``` |
482 | | /// use icu::locid::locale; |
483 | | /// use icu::plurals::{PluralCategory, PluralOperands}; |
484 | | /// use icu::plurals::{PluralRuleType, PluralRules}; |
485 | | /// # |
486 | | /// # let pr = PluralRules::try_new(&locale!("en").into(), PluralRuleType::Cardinal) |
487 | | /// # .expect("locale should be present"); |
488 | | /// |
489 | | /// let operands = PluralOperands::try_from(-5).expect("Failed to parse to operands."); |
490 | | /// let operands2: PluralOperands = "5.10".parse().expect("Failed to parse to operands."); |
491 | | /// |
492 | | /// assert_eq!(pr.category_for(operands), PluralCategory::Other); |
493 | | /// assert_eq!(pr.category_for(operands2), PluralCategory::Other); |
494 | | /// ``` |
495 | | /// |
496 | | /// [`Plural Category`]: PluralCategory |
497 | | /// [`Plural Operands`]: operands::PluralOperands |
498 | 0 | pub fn category_for<I: Into<PluralOperands>>(&self, input: I) -> PluralCategory { |
499 | 0 | let rules = self.0.get(); |
500 | 0 | let input = input.into(); |
501 | | |
502 | | macro_rules! test_rule { |
503 | | ($rule:ident, $cat:ident) => { |
504 | | rules |
505 | | .$rule |
506 | | .as_ref() |
507 | 0 | .and_then(|r| test_rule(r, &input).then(|| PluralCategory::$cat)) Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<icu_plurals::operands::PluralOperands>::{closure#4}Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<icu_plurals::operands::PluralOperands>::{closure#0}::{closure#0}Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<icu_plurals::operands::PluralOperands>::{closure#2}::{closure#0}Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<icu_plurals::operands::PluralOperands>::{closure#3}::{closure#0}Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<icu_plurals::operands::PluralOperands>::{closure#1}::{closure#0}Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<u32>::{closure#4}Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<u32>::{closure#0}::{closure#0}Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<u32>::{closure#2}::{closure#0}Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<u32>::{closure#3}::{closure#0}Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<u32>::{closure#1}::{closure#0}Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<_>::{closure#4}Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<_>::{closure#0}::{closure#0}Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<_>::{closure#2}::{closure#0}Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<_>::{closure#3}::{closure#0}Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<_>::{closure#1}::{closure#0} |
508 | | }; |
509 | | } |
510 | | |
511 | 0 | test_rule!(zero, Zero) |
512 | 0 | .or_else(|| test_rule!(one, One)) Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<icu_plurals::operands::PluralOperands>::{closure#0}Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<u32>::{closure#0}Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<_>::{closure#0} |
513 | 0 | .or_else(|| test_rule!(two, Two)) Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<icu_plurals::operands::PluralOperands>::{closure#1}Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<u32>::{closure#1}Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<_>::{closure#1} |
514 | 0 | .or_else(|| test_rule!(few, Few)) Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<icu_plurals::operands::PluralOperands>::{closure#2}Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<u32>::{closure#2}Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<_>::{closure#2} |
515 | 0 | .or_else(|| test_rule!(many, Many)) Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<icu_plurals::operands::PluralOperands>::{closure#3}Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<u32>::{closure#3}Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<_>::{closure#3} |
516 | 0 | .unwrap_or(PluralCategory::Other) |
517 | 0 | } Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<icu_plurals::operands::PluralOperands> Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<u32> Unexecuted instantiation: <icu_plurals::PluralRules>::category_for::<_> |
518 | | |
519 | | /// Returns all [`Plural Categories`] appropriate for a [`PluralRules`] object |
520 | | /// based on the [`LanguageIdentifier`](icu::locid::{LanguageIdentifier}) and [`PluralRuleType`]. |
521 | | /// |
522 | | /// The [`Plural Categories`] are returned in UTS 35 sorted order. |
523 | | /// |
524 | | /// The category [`PluralCategory::Other`] is always included. |
525 | | /// |
526 | | /// # Examples |
527 | | /// |
528 | | /// ``` |
529 | | /// use icu::locid::locale; |
530 | | /// use icu::plurals::{PluralCategory, PluralRuleType, PluralRules}; |
531 | | /// |
532 | | /// let pr = |
533 | | /// PluralRules::try_new(&locale!("fr").into(), PluralRuleType::Cardinal) |
534 | | /// .expect("locale should be present"); |
535 | | /// |
536 | | /// let mut categories = pr.categories(); |
537 | | /// assert_eq!(categories.next(), Some(PluralCategory::One)); |
538 | | /// assert_eq!(categories.next(), Some(PluralCategory::Many)); |
539 | | /// assert_eq!(categories.next(), Some(PluralCategory::Other)); |
540 | | /// assert_eq!(categories.next(), None); |
541 | | /// ``` |
542 | | /// |
543 | | /// [`Plural Categories`]: PluralCategory |
544 | 0 | pub fn categories(&self) -> impl Iterator<Item = PluralCategory> + '_ { |
545 | 0 | let rules = self.0.get(); |
546 | | |
547 | | macro_rules! test_rule { |
548 | | ($rule:ident, $cat:ident) => { |
549 | | rules |
550 | | .$rule |
551 | | .as_ref() |
552 | | .map(|_| PluralCategory::$cat) |
553 | | .into_iter() |
554 | | }; |
555 | | } |
556 | | |
557 | 0 | test_rule!(zero, Zero) |
558 | 0 | .chain(test_rule!(one, One)) |
559 | 0 | .chain(test_rule!(two, Two)) |
560 | 0 | .chain(test_rule!(few, Few)) |
561 | 0 | .chain(test_rule!(many, Many)) |
562 | 0 | .chain(Some(PluralCategory::Other)) |
563 | 0 | } |
564 | | } |
565 | | |
566 | | /// A [`PluralRules`] that also has the ability to retrieve an appropriate [`Plural Category`] for a |
567 | | /// range. |
568 | | /// |
569 | | /// ✨ *Enabled with the `experimental` Cargo feature.* |
570 | | /// |
571 | | /// <div class="stab unstable"> |
572 | | /// 🚧 This code is experimental; it may change at any time, in breaking or non-breaking ways, |
573 | | /// including in SemVer minor releases. Use with caution. |
574 | | /// <a href="https://github.com/unicode-org/icu4x/issues/4140">#4140</a> |
575 | | /// </div> |
576 | | /// |
577 | | /// # Examples |
578 | | /// |
579 | | /// ``` |
580 | | /// use icu::locid::locale; |
581 | | /// use icu::plurals::{PluralCategory, PluralOperands}; |
582 | | /// use icu::plurals::{PluralRuleType, PluralRulesWithRanges}; |
583 | | /// |
584 | | /// let ranges = PluralRulesWithRanges::try_new( |
585 | | /// &locale!("ar").into(), |
586 | | /// PluralRuleType::Cardinal, |
587 | | /// ) |
588 | | /// .expect("locale should be present"); |
589 | | /// |
590 | | /// let operands = PluralOperands::from(1_usize); |
591 | | /// let operands2: PluralOperands = |
592 | | /// "2.0".parse().expect("parsing to operands should succeed"); |
593 | | /// |
594 | | /// assert_eq!( |
595 | | /// ranges.category_for_range(operands, operands2), |
596 | | /// PluralCategory::Other |
597 | | /// ); |
598 | | /// ``` |
599 | | /// |
600 | | /// [`Plural Category`]: PluralCategory |
601 | | #[cfg(feature = "experimental")] |
602 | | #[derive(Debug)] |
603 | | pub struct PluralRulesWithRanges<R> { |
604 | | rules: R, |
605 | | ranges: DataPayload<PluralRangesV1Marker>, |
606 | | } |
607 | | |
608 | | #[cfg(feature = "experimental")] |
609 | | impl PluralRulesWithRanges<PluralRules> { |
610 | | icu_provider::gen_any_buffer_data_constructors!( |
611 | | locale: include, |
612 | | rule_type: PluralRuleType, |
613 | | error: PluralsError, |
614 | | /// Constructs a new `PluralRulesWithRanges` for a given locale using compiled data. |
615 | | /// |
616 | | /// ✨ *Enabled with the `compiled_data` Cargo feature.* |
617 | | /// |
618 | | /// [📚 Help choosing a constructor](icu_provider::constructors) |
619 | | /// |
620 | | /// # Examples |
621 | | /// |
622 | | /// ``` |
623 | | /// use icu::locid::locale; |
624 | | /// use icu::plurals::{PluralRuleType, PluralRulesWithRanges}; |
625 | | /// |
626 | | /// let _ = PluralRulesWithRanges::try_new( |
627 | | /// &locale!("en").into(), |
628 | | /// PluralRuleType::Cardinal, |
629 | | /// ).expect("locale should be present"); |
630 | | /// ``` |
631 | | ); |
632 | | |
633 | | #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::try_new)] |
634 | | pub fn try_new_unstable( |
635 | | provider: &(impl DataProvider<PluralRangesV1Marker> |
636 | | + DataProvider<CardinalV1Marker> |
637 | | + DataProvider<OrdinalV1Marker> |
638 | | + ?Sized), |
639 | | locale: &DataLocale, |
640 | | rule_type: PluralRuleType, |
641 | | ) -> Result<Self, PluralsError> { |
642 | | match rule_type { |
643 | | PluralRuleType::Cardinal => Self::try_new_cardinal_unstable(provider, locale), |
644 | | PluralRuleType::Ordinal => Self::try_new_ordinal_unstable(provider, locale), |
645 | | } |
646 | | } |
647 | | |
648 | | icu_provider::gen_any_buffer_data_constructors!( |
649 | | locale: include, |
650 | | options: skip, |
651 | | error: PluralsError, |
652 | | /// Constructs a new `PluralRulesWithRanges` for a given locale for cardinal numbers using |
653 | | /// compiled data. |
654 | | /// |
655 | | /// See [`PluralRules::try_new_cardinal`] for more information. |
656 | | /// |
657 | | /// ✨ *Enabled with the `compiled_data` Cargo feature.* |
658 | | /// |
659 | | /// [📚 Help choosing a constructor](icu_provider::constructors) |
660 | | /// |
661 | | /// # Examples |
662 | | /// |
663 | | /// ``` |
664 | | /// use icu::locid::locale; |
665 | | /// use icu::plurals::{PluralCategory, PluralRulesWithRanges}; |
666 | | /// |
667 | | /// let rules = PluralRulesWithRanges::try_new_cardinal(&locale!("ru").into()) |
668 | | /// .expect("locale should be present"); |
669 | | /// |
670 | | /// assert_eq!(rules.category_for_range(0_usize, 2_usize), PluralCategory::Few); |
671 | | /// ``` |
672 | | functions: [ |
673 | | try_new_cardinal, |
674 | | try_new_cardinal_with_any_provider, |
675 | | try_new_cardinal_with_buffer_provider, |
676 | | try_new_cardinal_unstable, |
677 | | Self, |
678 | | ] |
679 | | ); |
680 | | |
681 | | #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::try_new_cardinal)] |
682 | | pub fn try_new_cardinal_unstable( |
683 | | provider: &(impl DataProvider<CardinalV1Marker> + DataProvider<PluralRangesV1Marker> + ?Sized), |
684 | | locale: &DataLocale, |
685 | | ) -> Result<Self, PluralsError> { |
686 | | let rules = PluralRules::try_new_cardinal_unstable(provider, locale)?; |
687 | | |
688 | | PluralRulesWithRanges::try_new_with_rules_unstable(provider, locale, rules) |
689 | | } |
690 | | |
691 | | icu_provider::gen_any_buffer_data_constructors!( |
692 | | locale: include, |
693 | | options: skip, |
694 | | error: PluralsError, |
695 | | /// Constructs a new `PluralRulesWithRanges` for a given locale for ordinal numbers using |
696 | | /// compiled data. |
697 | | /// |
698 | | /// See [`PluralRules::try_new_ordinal`] for more information. |
699 | | /// |
700 | | /// ✨ *Enabled with the `compiled_data` Cargo feature.* |
701 | | /// |
702 | | /// [📚 Help choosing a constructor](icu_provider::constructors) |
703 | | /// |
704 | | /// # Examples |
705 | | /// |
706 | | /// ``` |
707 | | /// use icu::locid::locale; |
708 | | /// use icu::plurals::{PluralCategory, PluralRulesWithRanges}; |
709 | | /// |
710 | | /// let rules = PluralRulesWithRanges::try_new_ordinal( |
711 | | /// &locale!("ru").into(), |
712 | | /// ) |
713 | | /// .expect("locale should be present"); |
714 | | /// |
715 | | /// assert_eq!(rules.category_for_range(0_usize, 2_usize), PluralCategory::Other); |
716 | | /// ``` |
717 | | functions: [ |
718 | | try_new_ordinal, |
719 | | try_new_ordinal_with_any_provider, |
720 | | try_new_ordinal_with_buffer_provider, |
721 | | try_new_ordinal_unstable, |
722 | | Self, |
723 | | ] |
724 | | ); |
725 | | |
726 | | #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::try_new_ordinal)] |
727 | | pub fn try_new_ordinal_unstable( |
728 | | provider: &(impl DataProvider<OrdinalV1Marker> + DataProvider<PluralRangesV1Marker> + ?Sized), |
729 | | locale: &DataLocale, |
730 | | ) -> Result<Self, PluralsError> { |
731 | | let rules = PluralRules::try_new_ordinal_unstable(provider, locale)?; |
732 | | |
733 | | PluralRulesWithRanges::try_new_with_rules_unstable(provider, locale, rules) |
734 | | } |
735 | | } |
736 | | |
737 | | #[cfg(feature = "experimental")] |
738 | | impl<R> PluralRulesWithRanges<R> |
739 | | where |
740 | | R: AsRef<PluralRules>, |
741 | | { |
742 | | icu_provider::gen_any_buffer_data_constructors!( |
743 | | locale: include, |
744 | | rules: R, |
745 | | error: PluralsError, |
746 | | /// Constructs a new `PluralRulesWithRanges` for a given locale from an existing |
747 | | /// `PluralRules` (either owned or as a reference) and compiled data. |
748 | | /// |
749 | | /// # ⚠️ Warning |
750 | | /// |
751 | | /// The provided `locale` **MUST** be the same as the locale provided to the constructor |
752 | | /// of `rules`. Otherwise, [`Self::category_for_range`] will return incorrect results. |
753 | | /// |
754 | | /// ✨ *Enabled with the `compiled_data` Cargo feature.* |
755 | | /// |
756 | | /// [📚 Help choosing a constructor](icu_provider::constructors) |
757 | | /// |
758 | | /// # Examples |
759 | | /// |
760 | | /// ``` |
761 | | /// use icu::locid::locale; |
762 | | /// use icu::plurals::{PluralRuleType, PluralRulesWithRanges, PluralRules}; |
763 | | /// |
764 | | /// let rules = PluralRules::try_new(&locale!("en").into(), PluralRuleType::Cardinal) |
765 | | /// .expect("locale should be present"); |
766 | | /// |
767 | | /// let _ = |
768 | | /// PluralRulesWithRanges::try_new_with_rules(&locale!("en").into(), rules) |
769 | | /// .expect("locale should be present"); |
770 | | /// ``` |
771 | | functions: [ |
772 | | try_new_with_rules, |
773 | | try_new_with_rules_with_any_provider, |
774 | | try_new_with_rules_with_buffer_provider, |
775 | | try_new_with_rules_unstable, |
776 | | Self, |
777 | | ] |
778 | | ); |
779 | | |
780 | | #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::try_new_with_rules)] |
781 | | pub fn try_new_with_rules_unstable( |
782 | | provider: &(impl DataProvider<PluralRangesV1Marker> + ?Sized), |
783 | | locale: &DataLocale, |
784 | | rules: R, |
785 | | ) -> Result<Self, PluralsError> { |
786 | | let ranges = provider |
787 | | .load(DataRequest { |
788 | | locale, |
789 | | metadata: Default::default(), |
790 | | })? |
791 | | .take_payload()?; |
792 | | |
793 | | Ok(Self { rules, ranges }) |
794 | | } |
795 | | |
796 | | /// Gets a reference to the inner `PluralRules`. |
797 | | /// |
798 | | /// # Examples |
799 | | /// |
800 | | /// ``` |
801 | | /// use icu::locid::locale; |
802 | | /// use icu::plurals::{PluralCategory, PluralRulesWithRanges}; |
803 | | /// |
804 | | /// let ranges = PluralRulesWithRanges::try_new_cardinal(&locale!("en").into()) |
805 | | /// .expect("locale should be present"); |
806 | | /// |
807 | | /// let rules = ranges.rules(); |
808 | | /// |
809 | | /// assert_eq!(rules.category_for(1u8), PluralCategory::One); |
810 | | /// ``` |
811 | | pub fn rules(&self) -> &PluralRules { |
812 | | self.rules.as_ref() |
813 | | } |
814 | | |
815 | | /// Returns the [`Plural Category`] appropriate for a range. |
816 | | /// |
817 | | /// Note that the returned category is correct only if the range fulfills the following requirements: |
818 | | /// - The start value is strictly less than the end value. |
819 | | /// - Both values are positive. |
820 | | /// |
821 | | /// # Examples |
822 | | /// |
823 | | /// ``` |
824 | | /// use icu::locid::locale; |
825 | | /// use icu::plurals::{ |
826 | | /// PluralCategory, PluralOperands, PluralRuleType, PluralRulesWithRanges, |
827 | | /// }; |
828 | | /// |
829 | | /// let ranges = PluralRulesWithRanges::try_new( |
830 | | /// &locale!("ro").into(), |
831 | | /// PluralRuleType::Cardinal, |
832 | | /// ) |
833 | | /// .expect("locale should be present"); |
834 | | /// let operands: PluralOperands = |
835 | | /// "0.5".parse().expect("parsing to operands should succeed"); |
836 | | /// let operands2 = PluralOperands::from(1_usize); |
837 | | /// |
838 | | /// assert_eq!( |
839 | | /// ranges.category_for_range(operands, operands2), |
840 | | /// PluralCategory::Few |
841 | | /// ); |
842 | | /// ``` |
843 | | /// |
844 | | /// [`Plural Category`]: PluralCategory |
845 | | pub fn category_for_range<S: Into<PluralOperands>, E: Into<PluralOperands>>( |
846 | | &self, |
847 | | start: S, |
848 | | end: E, |
849 | | ) -> PluralCategory { |
850 | | let rules = self.rules.as_ref(); |
851 | | let start = rules.category_for(start); |
852 | | let end = rules.category_for(end); |
853 | | |
854 | | self.resolve_range(start, end) |
855 | | } |
856 | | |
857 | | /// Returns the [`Plural Category`] appropriate for a range from the categories of its endpoints. |
858 | | /// |
859 | | /// Note that the returned category is correct only if the original numeric range fulfills the |
860 | | /// following requirements: |
861 | | /// - The start value is strictly less than the end value. |
862 | | /// - Both values are positive. |
863 | | /// |
864 | | /// # Examples |
865 | | /// |
866 | | /// ``` |
867 | | /// use icu::locid::locale; |
868 | | /// use icu::plurals::{PluralCategory, PluralRuleType, PluralRulesWithRanges}; |
869 | | /// |
870 | | /// let ranges = PluralRulesWithRanges::try_new( |
871 | | /// &locale!("sl").into(), |
872 | | /// PluralRuleType::Ordinal, |
873 | | /// ) |
874 | | /// .expect("locale should be present"); |
875 | | /// |
876 | | /// assert_eq!( |
877 | | /// ranges.resolve_range(PluralCategory::Other, PluralCategory::One), |
878 | | /// PluralCategory::Few |
879 | | /// ); |
880 | | /// ``` |
881 | | /// |
882 | | /// [`Plural Category`]: PluralCategory |
883 | | pub fn resolve_range(&self, start: PluralCategory, end: PluralCategory) -> PluralCategory { |
884 | | self.ranges |
885 | | .get() |
886 | | .ranges |
887 | | .get_copied(&UnvalidatedPluralRange::from_range( |
888 | | start.into(), |
889 | | end.into(), |
890 | | )) |
891 | | .map(PluralCategory::from) |
892 | | .unwrap_or(end) |
893 | | } |
894 | | } |