icu_datetime/provider/skeleton/
plural.rs1use crate::provider::fields::{components, Field, FieldSymbol, Week};
6use crate::provider::pattern::{runtime::Pattern, PatternError, PatternItem};
7use either::Either;
8use icu_plurals::PluralCategory;
9use icu_provider::prelude::*;
10
11#[derive(Debug, PartialEq, Clone, yoke::Yokeable, zerofrom::ZeroFrom)]
13#[allow(missing_docs)]
14pub struct PluralPattern<'data> {
15 pub pivot_field: Week,
17
18 pub zero: Option<Pattern<'data>>,
19 pub one: Option<Pattern<'data>>,
20 pub two: Option<Pattern<'data>>,
21 pub few: Option<Pattern<'data>>,
22 pub many: Option<Pattern<'data>>,
23 pub other: Pattern<'data>,
24}
25
26#[allow(missing_docs)]
27impl<'data> PluralPattern<'data> {
28 pub fn new(pattern: Pattern<'data>) -> Result<Self, PatternError> {
30 let pivot_field = pattern
31 .items
32 .iter()
33 .find_map(|pattern_item| match pattern_item {
34 PatternItem::Field(Field {
35 symbol: FieldSymbol::Week(w),
36 ..
37 }) => Some(w),
38 _ => None,
39 })
40 .ok_or(PatternError::UnsupportedPluralPivot)?;
41
42 Ok(Self {
43 pivot_field,
44 zero: None,
45 one: None,
46 two: None,
47 few: None,
48 many: None,
49 other: pattern,
50 })
51 }
52
53 pub fn pivot_field(&self) -> Week {
55 self.pivot_field
56 }
57
58 pub fn maybe_set_variant(&mut self, category: PluralCategory, pattern: Pattern<'data>) {
59 if pattern == self.other {
60 return;
61 }
62 match category {
63 PluralCategory::Zero => self.zero = Some(pattern),
64 PluralCategory::One => self.one = Some(pattern),
65 PluralCategory::Two => self.two = Some(pattern),
66 PluralCategory::Few => self.few = Some(pattern),
67 PluralCategory::Many => self.many = Some(pattern),
68 PluralCategory::Other => unreachable!("You can't override other"),
69 }
70 }
71
72 pub fn patterns_iter(&self) -> impl Iterator<Item = &Pattern<'data>> {
73 PluralCategory::all().filter_map(move |cat| match cat {
74 PluralCategory::Zero => self.zero.as_ref(),
75 PluralCategory::One => self.one.as_ref(),
76 PluralCategory::Two => self.two.as_ref(),
77 PluralCategory::Few => self.few.as_ref(),
78 PluralCategory::Many => self.many.as_ref(),
79 PluralCategory::Other => Some(&self.other),
80 })
81 }
82
83 pub fn for_each_mut<F>(&mut self, f: &F)
84 where
85 F: Fn(&mut Pattern<'data>),
86 {
87 self.zero.iter_mut().for_each(f);
88 self.one.iter_mut().for_each(f);
89 self.two.iter_mut().for_each(f);
90 self.few.iter_mut().for_each(f);
91 self.many.iter_mut().for_each(f);
92 f(&mut self.other);
93 }
94
95 pub fn into_owned(self) -> PluralPattern<'static> {
96 PluralPattern {
97 pivot_field: self.pivot_field,
98 zero: self.zero.map(|p| p.into_owned()),
99 one: self.one.map(|p| p.into_owned()),
100 two: self.two.map(|p| p.into_owned()),
101 few: self.few.map(|p| p.into_owned()),
102 many: self.many.map(|p| p.into_owned()),
103 other: self.other.into_owned(),
104 }
105 }
106}
107
108#[derive(Debug, PartialEq, Clone, yoke::Yokeable, zerofrom::ZeroFrom)]
113#[allow(clippy::large_enum_variant)]
114pub enum PatternPlurals<'data> {
115 MultipleVariants(PluralPattern<'data>),
117 SinglePattern(Pattern<'data>),
119}
120
121impl From<&PatternPlurals<'_>> for components::Bag {
124 fn from(other: &PatternPlurals) -> Self {
125 let pattern = match other {
126 PatternPlurals::SinglePattern(pattern) => pattern,
127 PatternPlurals::MultipleVariants(plural_pattern) => &plural_pattern.other,
128 };
129 Self::from(pattern)
130 }
131}
132
133#[allow(missing_docs)]
134impl<'data> PatternPlurals<'data> {
135 pub fn into_owned(self) -> PatternPlurals<'static> {
136 match self {
137 Self::SinglePattern(pattern) => PatternPlurals::SinglePattern(pattern.into_owned()),
138 Self::MultipleVariants(plural_pattern) => {
139 PatternPlurals::MultipleVariants(plural_pattern.into_owned())
140 }
141 }
142 }
143
144 pub fn patterns_iter(&self) -> impl Iterator<Item = &Pattern<'data>> {
145 match self {
146 Self::SinglePattern(pattern) => Either::Left(core::iter::once(pattern)),
147 Self::MultipleVariants(plural_pattern) => Either::Right(plural_pattern.patterns_iter()),
148 }
149 }
150
151 pub fn for_each_mut<F>(&mut self, f: F)
152 where
153 F: Fn(&mut Pattern<'data>),
154 {
155 match self {
156 Self::SinglePattern(pattern) => f(pattern),
157 Self::MultipleVariants(variants) => variants.for_each_mut(&f),
158 }
159 }
160
161 pub fn expect_pattern(self, msg: &str) -> Pattern<'data> {
162 match self {
163 Self::SinglePattern(pattern) => pattern,
164
165 Self::MultipleVariants(patterns) => {
166 debug_assert!(
168 false,
169 "expect_pattern called with bad data (falling back to `other` pattern): {msg}"
170 );
171 patterns.other
172 }
173 }
174 }
175
176 pub fn normalize(&mut self) {
178 if let Self::MultipleVariants(patterns) = self {
179 if patterns.patterns_iter().count() == 1 {
180 *self = Self::SinglePattern(core::mem::take(&mut patterns.other));
181 }
182 }
183 }
184}
185
186impl<'data> From<Pattern<'data>> for PatternPlurals<'data> {
187 fn from(pattern: Pattern<'data>) -> Self {
188 Self::SinglePattern(pattern)
189 }
190}
191
192impl<'data> From<PluralPattern<'data>> for PatternPlurals<'data> {
193 fn from(pattern: PluralPattern<'data>) -> Self {
194 Self::MultipleVariants(pattern)
195 }
196}
197
198impl Default for PatternPlurals<'_> {
199 fn default() -> Self {
200 PatternPlurals::SinglePattern(Pattern::default())
201 }
202}
203
204#[cfg(test)]
205mod test {
206 use super::*;
207
208 #[test]
209 #[ignore] #[allow(unreachable_code, unused_variables, unused_mut)]
211 fn build_plural_pattern() {
212 let red_pattern: Pattern = "'red' w".parse().unwrap();
213 let blue_pattern: Pattern = "'blue' w".parse().unwrap();
214 let mut patterns =
215 PluralPattern::new(blue_pattern.clone()).expect("PluralPattern::new failed");
216 patterns.maybe_set_variant(PluralCategory::Zero, red_pattern.clone());
217 patterns.maybe_set_variant(PluralCategory::One, blue_pattern.clone());
218 patterns.maybe_set_variant(PluralCategory::Two, red_pattern.clone());
219 patterns.maybe_set_variant(PluralCategory::Few, red_pattern.clone());
220 patterns.maybe_set_variant(PluralCategory::Many, blue_pattern.clone());
221
222 assert_eq!(patterns.zero, Some(red_pattern.clone()));
224 assert_eq!(patterns.one, None); assert_eq!(patterns.two, Some(red_pattern));
226 assert_eq!(patterns.other, blue_pattern);
227 }
228
229 #[test]
230 #[ignore] #[allow(unreachable_code, unused_variables)]
232 fn normalize_pattern_plurals_switches_singletons_to_single_pattern() {
233 let pattern: Pattern = "'red' w".parse().unwrap();
234 let patterns = PluralPattern::new(pattern.clone()).expect("PluralPattern::new failed");
235 let mut plural_patterns: PatternPlurals = PatternPlurals::MultipleVariants(patterns);
236
237 plural_patterns.normalize();
238
239 assert_eq!(plural_patterns, PatternPlurals::SinglePattern(pattern));
240 }
241}