Coverage Report

Created: 2025-07-12 07:16

/rust/registry/src/index.crates.io-6f17d22bba15001f/icu_plurals-1.5.0/src/provider.rs
Line
Count
Source (jump to first uncovered line)
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
// Provider structs must be stable
6
#![allow(clippy::exhaustive_structs, clippy::exhaustive_enums)]
7
8
//! 🚧 \[Unstable\] Data provider struct definitions for this ICU4X component.
9
//!
10
//! <div class="stab unstable">
11
//! 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
12
//! including in SemVer minor releases. While the serde representation of data structs is guaranteed
13
//! to be stable, their Rust representation might not be. Use with caution.
14
//! </div>
15
//!
16
//! Read more about data providers: [`icu_provider`]
17
18
use crate::rules::runtime::ast::Rule;
19
use icu_provider::prelude::*;
20
use icu_provider::DataMarker;
21
22
#[cfg(feature = "compiled_data")]
23
#[derive(Debug)]
24
/// Baked data
25
///
26
/// <div class="stab unstable">
27
/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
28
/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
29
/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
30
/// </div>
31
pub struct Baked;
32
33
#[cfg(feature = "compiled_data")]
34
const _: () = {
35
    pub mod icu {
36
        pub use crate as plurals;
37
        #[allow(unused_imports)] // baked data may or may not need this
38
        pub use icu_locid_transform as locid_transform;
39
    }
40
    icu_plurals_data::make_provider!(Baked);
41
    icu_plurals_data::impl_plurals_ordinal_v1!(Baked);
42
    icu_plurals_data::impl_plurals_cardinal_v1!(Baked);
43
    #[cfg(feature = "experimental")]
44
    icu_plurals_data::impl_plurals_ranges_v1!(Baked);
45
};
46
47
#[cfg(feature = "datagen")]
48
/// The latest minimum set of keys required by this component.
49
pub const KEYS: &[DataKey] = &[
50
    CardinalV1Marker::KEY,
51
    OrdinalV1Marker::KEY,
52
    #[cfg(feature = "experimental")]
53
    PluralRangesV1Marker::KEY,
54
];
55
56
/// Plural rule strings conforming to UTS 35 syntax. Includes separate fields for five of the six
57
/// standard plural forms. If none of the rules match, the "other" category is assumed.
58
///
59
/// More information: <https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules>
60
///
61
/// <div class="stab unstable">
62
/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
63
/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
64
/// to be stable, their Rust representation might not be. Use with caution.
65
/// </div>
66
0
#[icu_provider::data_struct(
67
0
    CardinalV1Marker = "plurals/cardinal@1",
68
0
    OrdinalV1Marker = "plurals/ordinal@1"
69
0
)]
Unexecuted instantiation: <icu_plurals::provider::PluralRulesV1 as yoke::yokeable::Yokeable>::transform
Unexecuted instantiation: <icu_plurals::provider::PluralRulesV1 as zerofrom::zero_from::ZeroFrom<icu_plurals::provider::PluralRulesV1>>::zero_from
Unexecuted instantiation: <icu_plurals::provider::PluralRulesV1 as yoke::yokeable::Yokeable>::transform_owned
Unexecuted instantiation: <icu_plurals::provider::PluralRulesV1 as yoke::yokeable::Yokeable>::transform_mut::<_>
Unexecuted instantiation: <icu_plurals::provider::PluralRulesV1 as yoke::yokeable::Yokeable>::make
Unexecuted instantiation: <icu_plurals::provider::PluralRulesV1 as yoke::yokeable::Yokeable>::make
70
#[derive(Default, Clone, PartialEq, Debug)]
71
#[cfg_attr(
72
    feature = "datagen",
73
    derive(serde::Serialize, databake::Bake),
74
    databake(path = icu_plurals::provider),
75
)]
76
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
77
pub struct PluralRulesV1<'data> {
78
    /// Rule that matches [`PluralCategory::Zero`](super::PluralCategory::Zero), or `None` if not present.
79
    #[cfg_attr(feature = "serde", serde(borrow))]
80
    pub zero: Option<Rule<'data>>,
81
    /// Rule that matches [`PluralCategory::One`](super::PluralCategory::One), or `None` if not present.
82
    #[cfg_attr(feature = "serde", serde(borrow))]
83
    pub one: Option<Rule<'data>>,
84
    /// Rule that matches [`PluralCategory::Two`](super::PluralCategory::Two), or `None` if not present.
85
    #[cfg_attr(feature = "serde", serde(borrow))]
86
    pub two: Option<Rule<'data>>,
87
    /// Rule that matches [`PluralCategory::Few`](super::PluralCategory::Few), or `None` if not present.
88
    #[cfg_attr(feature = "serde", serde(borrow))]
89
    pub few: Option<Rule<'data>>,
90
    /// Rule that matches [`PluralCategory::Many`](super::PluralCategory::Many), or `None` if not present.
91
    #[cfg_attr(feature = "serde", serde(borrow))]
92
    pub many: Option<Rule<'data>>,
93
}
94
95
pub(crate) struct ErasedPluralRulesV1Marker;
96
97
impl DataMarker for ErasedPluralRulesV1Marker {
98
    type Yokeable = PluralRulesV1<'static>;
99
}
100
101
#[cfg(any(feature = "datagen", feature = "experimental"))]
102
pub use ranges::*;
103
104
#[cfg(any(feature = "datagen", feature = "experimental"))]
105
mod ranges {
106
    use crate::PluralCategory;
107
    use icu_provider::prelude::*;
108
    use zerovec::ZeroMap;
109
110
    /// [`PluralCategory`] but serializable as provider data.
111
    ///
112
    /// <div class="stab unstable">
113
    /// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
114
    /// including in SemVer minor releases. While the serde representation of data structs is guaranteed
115
    /// to be stable, their Rust representation might not be. Use with caution.
116
    /// </div>
117
    #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Ord, PartialOrd)]
118
    #[cfg_attr(
119
        feature = "datagen",
120
        derive(serde::Serialize, databake::Bake),
121
        databake(path = icu_plurals::provider),
122
    )]
123
    #[cfg_attr(feature = "serde", derive(serde::Deserialize))]
124
    #[zerovec::make_ule(RawPluralCategoryULE)]
125
    #[repr(u8)]
126
    #[cfg_attr(
127
        any(feature = "datagen", feature = "serde"),
128
        serde(rename_all = "lowercase")
129
    )]
130
    pub enum RawPluralCategory {
131
        /// CLDR "other" plural category.
132
        Other = 0,
133
        /// CLDR "zero" plural category.
134
        Zero = 1,
135
        /// CLDR "one" plural category.
136
        One = 2,
137
        /// CLDR "two" plural category.
138
        Two = 3,
139
        /// CLDR "few" plural category.
140
        Few = 4,
141
        /// CLDR "many" plural category.
142
        Many = 5,
143
    }
144
145
    impl RawPluralCategory {
146
        /// Gets the corresponding variant string of this `RawPluralCategory`.
147
        #[cfg(any(feature = "datagen", feature = "serde"))]
148
        const fn as_str(self) -> &'static str {
149
            match self {
150
                Self::Other => "other",
151
                Self::Zero => "zero",
152
                Self::One => "one",
153
                Self::Two => "two",
154
                Self::Few => "few",
155
                Self::Many => "many",
156
            }
157
        }
158
    }
159
160
    impl From<RawPluralCategory> for PluralCategory {
161
        fn from(value: RawPluralCategory) -> Self {
162
            match value {
163
                RawPluralCategory::Other => PluralCategory::Other,
164
                RawPluralCategory::Zero => PluralCategory::Zero,
165
                RawPluralCategory::One => PluralCategory::One,
166
                RawPluralCategory::Two => PluralCategory::Two,
167
                RawPluralCategory::Few => PluralCategory::Few,
168
                RawPluralCategory::Many => PluralCategory::Many,
169
            }
170
        }
171
    }
172
173
    impl From<PluralCategory> for RawPluralCategory {
174
        fn from(value: PluralCategory) -> Self {
175
            match value {
176
                PluralCategory::Zero => RawPluralCategory::Zero,
177
                PluralCategory::One => RawPluralCategory::One,
178
                PluralCategory::Two => RawPluralCategory::Two,
179
                PluralCategory::Few => RawPluralCategory::Few,
180
                PluralCategory::Many => RawPluralCategory::Many,
181
                PluralCategory::Other => RawPluralCategory::Other,
182
            }
183
        }
184
    }
185
186
    /// An `u8` that is expected to be a plural range, but does not enforce this invariant.
187
    ///
188
    /// <div class="stab unstable">
189
    /// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
190
    /// including in SemVer minor releases. While the serde representation of data structs is guaranteed
191
    /// to be stable, their Rust representation might not be. Use with caution.
192
    /// </div>
193
    #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Ord, PartialOrd)]
194
    #[cfg_attr(
195
        feature = "datagen",
196
        derive(databake::Bake),
197
        databake(path = icu_plurals::provider),
198
    )]
199
    #[zerovec::make_ule(UnvalidatedPluralRangeULE)]
200
    pub struct UnvalidatedPluralRange(pub u8);
201
202
    impl UnvalidatedPluralRange {
203
        /// Creates a new `UnvalidatedPluralRange` from a category range.
204
        pub fn from_range(start: RawPluralCategory, end: RawPluralCategory) -> Self {
205
            let start = start as u8;
206
            let end = end as u8;
207
208
            debug_assert!(start < 16);
209
            debug_assert!(end < 16);
210
211
            let range = (start << 4) | end;
212
213
            Self(range)
214
        }
215
    }
216
217
    #[cfg(feature = "datagen")]
218
    impl serde::Serialize for UnvalidatedPluralRange {
219
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
220
        where
221
            S: serde::Serializer,
222
        {
223
            use serde::ser::Error;
224
225
            struct PrettyPrinter(RawPluralCategory, RawPluralCategory);
226
227
            impl core::fmt::Display for PrettyPrinter {
228
                fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
229
                    f.write_str(self.0.as_str())?;
230
                    f.write_str("--")?;
231
                    f.write_str(self.1.as_str())
232
                }
233
            }
234
235
            if serializer.is_human_readable() {
236
                let start = RawPluralCategory::new_from_u8(self.0 >> 4)
237
                    .ok_or_else(|| S::Error::custom("invalid tag in UnvalidatedPluralRange"))?;
238
                let end = RawPluralCategory::new_from_u8(self.0 & 0x0F)
239
                    .ok_or_else(|| S::Error::custom("invalid tag in UnvalidatedPluralRange"))?;
240
                serializer.collect_str(&PrettyPrinter(start, end))
241
            } else {
242
                self.0.serialize(serializer)
243
            }
244
        }
245
    }
246
247
    #[cfg(feature = "serde")]
248
    impl<'de> serde::Deserialize<'de> for UnvalidatedPluralRange {
249
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
250
        where
251
            D: serde::Deserializer<'de>,
252
        {
253
            use serde::de::{Error, Visitor};
254
255
            struct HumanReadableVisitor;
256
257
            impl<'de> Visitor<'de> for HumanReadableVisitor {
258
                type Value = UnvalidatedPluralRange;
259
260
                fn expecting(&self, formatter: &mut alloc::fmt::Formatter) -> alloc::fmt::Result {
261
                    write!(
262
                        formatter,
263
                        "a plural range of the form <PluralCategory>-<PluralCategory>",
264
                    )
265
                }
266
267
                fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
268
                where
269
                    E: Error,
270
                {
271
                    const VARIANTS: [&str; 6] = [
272
                        RawPluralCategory::Other.as_str(),
273
                        RawPluralCategory::Zero.as_str(),
274
                        RawPluralCategory::One.as_str(),
275
                        RawPluralCategory::Two.as_str(),
276
                        RawPluralCategory::Few.as_str(),
277
                        RawPluralCategory::Many.as_str(),
278
                    ];
279
280
                    let (start, end) = v
281
                        .split_once("--")
282
                        .ok_or_else(|| E::custom("expected token `--` in plural range"))?;
283
284
                    let start = PluralCategory::get_for_cldr_string(start)
285
                        .ok_or_else(|| E::unknown_variant(start, &VARIANTS))?;
286
                    let end = PluralCategory::get_for_cldr_string(end)
287
                        .ok_or_else(|| E::unknown_variant(end, &VARIANTS))?;
288
289
                    Ok(UnvalidatedPluralRange::from_range(start.into(), end.into()))
290
                }
291
            }
292
293
            if deserializer.is_human_readable() {
294
                deserializer.deserialize_str(HumanReadableVisitor)
295
            } else {
296
                Ok(Self(<u8>::deserialize(deserializer)?))
297
            }
298
        }
299
    }
300
301
    /// Plural categories for ranges.
302
    ///
303
    /// Obtains the plural category of a range from the categories of its endpoints. It is required that
304
    /// the start value must be strictly less than the end value, and both values must be strictly positive.
305
    ///
306
    /// More information: <https://unicode.org/reports/tr35/tr35-numbers.html#Plural_Ranges>
307
    ///
308
    /// <div class="stab unstable">
309
    /// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
310
    /// including in SemVer minor releases. While the serde representation of data structs is guaranteed
311
    /// to be stable, their Rust representation might not be. Use with caution.
312
    /// </div>
313
    #[icu_provider::data_struct(PluralRangesV1Marker = "plurals/ranges@1")]
314
    #[derive(Clone, PartialEq, Debug)]
315
    #[cfg_attr(
316
        feature = "datagen",
317
        derive(serde::Serialize, databake::Bake),
318
        databake(path = icu_plurals::provider),
319
    )]
320
    #[cfg_attr(feature = "serde", derive(serde::Deserialize))]
321
    #[yoke(prove_covariance_manually)]
322
    pub struct PluralRangesV1<'data> {
323
        /// Map between the categories of the endpoints of a range and its corresponding
324
        /// category.
325
        ///
326
        /// This is roughly equivalent to a `BTreeMap<(PluralCategory, PluralCategory), PluralCategory>`,
327
        /// where the key is `(start category, end category)`.
328
        #[cfg_attr(feature = "serde", serde(borrow))]
329
        pub ranges: ZeroMap<'data, UnvalidatedPluralRange, RawPluralCategory>,
330
    }
331
}