• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zbraniecki / icu4x / 9014530096

08 May 2024 07:27PM UTC coverage: 76.402% (+0.2%) from 76.234%
9014530096

push

github

web-flow
Add missing std pointer-like impls for DataProvider, DynamicDataProvider (#4880)

0 of 3 new or added lines in 1 file covered. (0.0%)

3218 existing lines in 167 files now uncovered.

53328 of 69799 relevant lines covered (76.4%)

504343.42 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.11
/components/datetime/src/pattern/runtime/plural.rs
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
use crate::{
6
    fields::{Field, FieldSymbol, Week},
7
    pattern::{runtime::Pattern, PatternError, PatternItem},
8
};
9
use either::Either;
10
use icu_plurals::PluralCategory;
11
use icu_provider::prelude::*;
12

13
/// A collection of plural variants of a pattern.
14
#[derive(Debug, PartialEq, Clone, yoke::Yokeable, zerofrom::ZeroFrom)]
126✔
15
#[cfg_attr(
16
    feature = "datagen",
17
    derive(serde::Serialize, databake::Bake),
5✔
18
    databake(path = icu_datetime::pattern::runtime),
19
)]
20
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
29✔
21
#[allow(clippy::exhaustive_structs)] // part of data struct
22
pub struct PluralPattern<'data> {
23
    /// The field that 'variants' are predicated on.
24
    pub pivot_field: Week,
56✔
25

26
    #[cfg_attr(feature = "serde", serde(borrow))]
27
    pub zero: Option<Pattern<'data>>,
56✔
28
    #[cfg_attr(feature = "serde", serde(borrow))]
29
    pub one: Option<Pattern<'data>>,
56✔
30
    #[cfg_attr(feature = "serde", serde(borrow))]
31
    pub two: Option<Pattern<'data>>,
56✔
32
    #[cfg_attr(feature = "serde", serde(borrow))]
33
    pub few: Option<Pattern<'data>>,
56✔
34
    #[cfg_attr(feature = "serde", serde(borrow))]
35
    pub many: Option<Pattern<'data>>,
56✔
36
    #[cfg_attr(feature = "serde", serde(borrow))]
37
    pub other: Pattern<'data>,
56✔
38
}
39

40
impl<'data> PluralPattern<'data> {
41
    pub fn new(pattern: Pattern<'data>) -> Result<Self, PatternError> {
11,279✔
42
        let pivot_field = pattern
11,279✔
43
            .items
44
            .iter()
45
            .find_map(|pattern_item| match pattern_item {
71,315✔
46
                PatternItem::Field(Field {
47
                    symbol: FieldSymbol::Week(w),
11,279✔
48
                    ..
49
                }) => Some(w),
11,279✔
50
                _ => None,
60,036✔
51
            })
71,315✔
52
            .ok_or(PatternError::UnsupportedPluralPivot)?;
11,279✔
53

54
        Ok(Self {
11,279✔
55
            pivot_field,
56
            zero: None,
11,279✔
57
            one: None,
11,279✔
58
            two: None,
11,279✔
59
            few: None,
11,279✔
60
            many: None,
11,279✔
61
            other: pattern,
11,279✔
62
        })
63
    }
11,279✔
64

65
    /// Returns which week field determines the [icu_plurals::PluralCategory] used to select a pattern variant for a given date.
66
    pub fn pivot_field(&self) -> Week {
60✔
67
        self.pivot_field
60✔
68
    }
60✔
69

70
    pub fn maybe_set_variant(&mut self, category: PluralCategory, pattern: Pattern<'data>) {
20,930✔
71
        if pattern == self.other {
20,930✔
72
            return;
73
        }
74
        match category {
812✔
75
            PluralCategory::Zero => self.zero = Some(pattern),
1✔
76
            PluralCategory::One => self.one = Some(pattern),
807✔
77
            PluralCategory::Two => self.two = Some(pattern),
3✔
78
            PluralCategory::Few => self.few = Some(pattern),
1✔
UNCOV
79
            PluralCategory::Many => self.many = Some(pattern),
×
UNCOV
80
            PluralCategory::Other => unreachable!("You can't override other"),
×
81
        }
82
    }
20,930✔
83

84
    pub(crate) fn variant(&self, category: PluralCategory) -> &Pattern<'data> {
60✔
85
        let variant = match category {
60✔
UNCOV
86
            PluralCategory::Zero => &self.zero,
×
UNCOV
87
            PluralCategory::One => &self.one,
×
88
            PluralCategory::Two => &self.two,
×
89
            PluralCategory::Few => &self.few,
×
90
            PluralCategory::Many => &self.many,
×
91
            PluralCategory::Other => return &self.other,
60✔
92
        };
UNCOV
93
        variant.as_ref().unwrap_or(&self.other)
×
94
    }
60✔
95

96
    pub fn patterns_iter(&self) -> impl Iterator<Item = &Pattern<'data>> {
11,282✔
97
        PluralCategory::all().filter_map(move |cat| match cat {
78,976✔
98
            PluralCategory::Zero => self.zero.as_ref(),
11,283✔
99
            PluralCategory::One => self.one.as_ref(),
11,280✔
100
            PluralCategory::Two => self.two.as_ref(),
11,282✔
101
            PluralCategory::Few => self.few.as_ref(),
11,283✔
102
            PluralCategory::Many => self.many.as_ref(),
11,283✔
103
            PluralCategory::Other => Some(&self.other),
11,283✔
104
        })
67,694✔
105
    }
11,282✔
106

107
    pub fn for_each_mut<F>(&mut self, f: &F)
91✔
108
    where
109
        F: Fn(&mut Pattern<'data>),
110
    {
111
        self.zero.iter_mut().for_each(f);
91✔
112
        self.one.iter_mut().for_each(f);
91✔
113
        self.two.iter_mut().for_each(f);
91✔
114
        self.few.iter_mut().for_each(f);
91✔
115
        self.many.iter_mut().for_each(f);
91✔
116
        f(&mut self.other);
91✔
117
    }
91✔
118

119
    pub fn into_owned(self) -> PluralPattern<'static> {
7✔
120
        PluralPattern {
7✔
121
            pivot_field: self.pivot_field,
7✔
122
            zero: self.zero.map(|p| p.into_owned()),
7✔
123
            one: self.one.map(|p| p.into_owned()),
14✔
124
            two: self.two.map(|p| p.into_owned()),
7✔
125
            few: self.few.map(|p| p.into_owned()),
7✔
126
            many: self.many.map(|p| p.into_owned()),
7✔
127
            other: self.other.into_owned(),
7✔
UNCOV
128
        }
×
129
    }
7✔
130
}
131

132
/// Either a [`Pattern`] single pattern or a [`PluralPattern`] collection of
133
/// patterns when there are plural variants.
134
///
135
/// Currently, the plural forms are only based on the week number.
136
#[derive(Debug, PartialEq, Clone, yoke::Yokeable, zerofrom::ZeroFrom)]
82,038✔
137
#[allow(clippy::large_enum_variant)]
138
#[allow(clippy::exhaustive_enums)] // this type is stable
139
#[cfg_attr(
140
    feature = "datagen",
UNCOV
141
    derive(databake::Bake),
×
142
    databake(path = icu_datetime::pattern::runtime),
143
)]
144
pub enum PatternPlurals<'data> {
145
    /// A collection of pattern variants for when plurals differ.
146
    MultipleVariants(PluralPattern<'data>),
62✔
147
    /// A single pattern.
148
    SinglePattern(Pattern<'data>),
81,195✔
149
}
150

151
impl<'data> PatternPlurals<'data> {
152
    pub fn into_owned(self) -> PatternPlurals<'static> {
305✔
153
        match self {
305✔
154
            Self::SinglePattern(pattern) => PatternPlurals::SinglePattern(pattern.into_owned()),
298✔
155
            Self::MultipleVariants(plural_pattern) => {
7✔
156
                PatternPlurals::MultipleVariants(plural_pattern.into_owned())
7✔
157
            }
7✔
158
        }
159
    }
305✔
160

161
    pub fn patterns_iter(&self) -> impl Iterator<Item = &Pattern<'data>> {
885✔
162
        match self {
885✔
163
            Self::SinglePattern(pattern) => Either::Left(core::iter::once(pattern)),
877✔
164
            Self::MultipleVariants(plural_pattern) => Either::Right(plural_pattern.patterns_iter()),
8✔
165
        }
166
    }
885✔
167

168
    pub fn for_each_mut<F>(&mut self, f: F)
30,525✔
169
    where
170
        F: Fn(&mut Pattern<'data>),
171
    {
172
        match self {
30,525✔
173
            Self::SinglePattern(pattern) => f(pattern),
30,434✔
174
            Self::MultipleVariants(variants) => variants.for_each_mut(&f),
91✔
175
        }
176
    }
30,525✔
177

178
    pub fn expect_pattern(self, msg: &str) -> Pattern<'data> {
15,246✔
179
        match self {
15,246✔
180
            Self::SinglePattern(pattern) => pattern,
15,246✔
181

UNCOV
182
            Self::MultipleVariants(patterns) => {
×
183
                // Potentially change to log::warn! in #2648
UNCOV
184
                debug_assert!(
×
185
                    false,
186
                    "expect_pattern called with bad data (falling back to `other` pattern): {msg}"
187
                );
188
                patterns.other
UNCOV
189
            }
×
190
        }
191
    }
15,246✔
192

193
    // Removes redundant patterns & transforms singleton [PatternPlurals::MultipleVariants] into a [PatternPlurals::SinglePattern].
194
    pub fn normalize(&mut self) {
327,992✔
195
        if let Self::MultipleVariants(patterns) = self {
327,992✔
196
            if patterns.patterns_iter().count() == 1 {
21,742✔
197
                *self = Self::SinglePattern(core::mem::take(&mut patterns.other));
10,468✔
198
            }
199
        }
200
    }
327,992✔
201
}
202

203
impl<'data> From<Pattern<'data>> for PatternPlurals<'data> {
204
    fn from(pattern: Pattern<'data>) -> Self {
1,191✔
205
        Self::SinglePattern(pattern)
1,191✔
206
    }
1,191✔
207
}
208

209
impl<'data> From<PluralPattern<'data>> for PatternPlurals<'data> {
210
    fn from(pattern: PluralPattern<'data>) -> Self {
3✔
211
        Self::MultipleVariants(pattern)
3✔
212
    }
3✔
213
}
214

215
impl Default for PatternPlurals<'_> {
UNCOV
216
    fn default() -> Self {
×
UNCOV
217
        PatternPlurals::SinglePattern(Pattern::default())
×
UNCOV
218
    }
×
219
}
220

221
#[cfg(test)]
222
mod test {
223
    use super::*;
224

225
    #[test]
226
    fn build_plural_pattern() {
2✔
227
        let red_pattern: Pattern = "'red' w".parse().unwrap();
1✔
228
        let blue_pattern: Pattern = "'blue' w".parse().unwrap();
1✔
229
        let mut patterns =
230
            PluralPattern::new(blue_pattern.clone()).expect("PluralPattern::new failed");
1✔
231
        patterns.maybe_set_variant(PluralCategory::Zero, red_pattern.clone());
1✔
232
        patterns.maybe_set_variant(PluralCategory::One, blue_pattern.clone());
1✔
233
        patterns.maybe_set_variant(PluralCategory::Two, red_pattern.clone());
1✔
234
        patterns.maybe_set_variant(PluralCategory::Few, red_pattern.clone());
1✔
235
        patterns.maybe_set_variant(PluralCategory::Many, blue_pattern.clone());
1✔
236

237
        assert_eq!(patterns.pivot_field, Week::WeekOfYear);
1✔
238
        assert_eq!(patterns.zero, Some(red_pattern.clone()));
1✔
239
        assert_eq!(patterns.one, None); // duplicate `other
1✔
240
        assert_eq!(patterns.two, Some(red_pattern));
1✔
241
        assert_eq!(patterns.other, blue_pattern);
1✔
242
    }
2✔
243

244
    #[test]
245
    fn normalize_pattern_plurals_switches_singletons_to_single_pattern() {
2✔
246
        let pattern: Pattern = "'red' w".parse().unwrap();
1✔
247
        let patterns = PluralPattern::new(pattern.clone()).expect("PluralPattern::new failed");
1✔
248
        let mut plural_patterns: PatternPlurals = PatternPlurals::MultipleVariants(patterns);
1✔
249

250
        plural_patterns.normalize();
1✔
251

252
        assert_eq!(plural_patterns, PatternPlurals::SinglePattern(pattern));
1✔
253
    }
2✔
254
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc