icu_list/provider/
mod.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// 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
18use icu_provider::prelude::*;
19
20mod serde_dfa;
21pub use serde_dfa::SerdeDFA;
22use zerovec::VarZeroCow;
23
24#[cfg(feature = "compiled_data")]
25#[derive(Debug)]
26/// Baked data
27///
28/// <div class="stab unstable">
29/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
30/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
31/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
32/// </div>
33pub struct Baked;
34
35#[cfg(feature = "compiled_data")]
36#[allow(unused_imports)]
37const _: () = {
38    use icu_list_data::*;
39    pub mod icu {
40        pub use crate as list;
41        pub use icu_locale as locale;
42    }
43    make_provider!(Baked);
44    impl_list_and_v1!(Baked);
45    impl_list_or_v1!(Baked);
46    impl_list_unit_v1!(Baked);
47};
48
49#[cfg(feature = "datagen")]
50/// The latest minimum set of markers required by this component.
51pub const MARKERS: &[DataMarkerInfo] = &[ListAndV1::INFO, ListOrV1::INFO, ListUnitV1::INFO];
52
53data_marker!(
54    /// Marker for and lists
55    ListAndV1,
56    "list/and/v1",
57    ListFormatterPatterns<'static>,
58);
59data_marker!(
60    /// Marker for or lists
61    ListOrV1,
62    "list/or/v1",
63    ListFormatterPatterns<'static>,
64);
65data_marker!(
66    /// Marker for unit lists
67    ListUnitV1,
68    "list/unit/v1",
69    ListFormatterPatterns<'static>,
70);
71
72icu_provider::data_struct!(
73    ListFormatterPatterns<'_>,
74    #[cfg(feature = "datagen")]
75);
76
77/// Symbols and metadata required for [`ListFormatter`](crate::ListFormatter).
78///
79/// <div class="stab unstable">
80/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
81/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
82/// to be stable, their Rust representation might not be. Use with caution.
83/// </div>
84#[derive(Clone, Debug, PartialEq, yoke::Yokeable, zerofrom::ZeroFrom)]
85#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
86#[cfg_attr(feature = "datagen", databake(path = icu_list::provider))]
87#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
88pub struct ListFormatterPatterns<'data> {
89    /// The start pattern
90    #[cfg_attr(feature = "serde", serde(borrow))]
91    pub start: ListJoinerPattern<'data>,
92    /// The middle pattern. It doesn't need to be a pattern because it has to start with `{0}`
93    /// and end with `{1}`, so we just store the string in between.
94    #[cfg_attr(feature = "serde", serde(borrow))]
95    pub middle: VarZeroCow<'data, str>,
96    /// The end pattern
97    #[cfg_attr(feature = "serde", serde(borrow))]
98    pub end: ConditionalListJoinerPattern<'data>,
99    /// The pair pattern, if it's different from the end pattern.
100    #[cfg_attr(feature = "serde", serde(borrow))]
101    pub pair: Option<ConditionalListJoinerPattern<'data>>,
102}
103
104impl ListFormatterPatterns<'_> {
105    /// The marker attributes for narrow lists
106    pub const NARROW: &'static DataMarkerAttributes = DataMarkerAttributes::from_str_or_panic("N");
107    #[doc(hidden)]
108    pub const NARROW_STR: &'static str = Self::NARROW.as_str();
109    /// The marker attributes for short lists
110    pub const SHORT: &'static DataMarkerAttributes = DataMarkerAttributes::from_str_or_panic("S");
111    #[doc(hidden)]
112    pub const SHORT_STR: &'static str = Self::SHORT.as_str();
113    /// The marker attributes for wide lists
114    pub const WIDE: &'static DataMarkerAttributes = DataMarkerAttributes::from_str_or_panic("W");
115    #[doc(hidden)]
116    pub const WIDE_STR: &'static str = Self::WIDE.as_str();
117}
118
119/// A pattern that can behave conditionally on the next element.
120///
121/// <div class="stab unstable">
122/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
123/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
124/// to be stable, their Rust representation might not be. Use with caution.
125/// </div>
126#[derive(Clone, Debug, PartialEq, yoke::Yokeable, zerofrom::ZeroFrom)]
127#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
128#[cfg_attr(feature = "datagen", databake(path = icu_list::provider))]
129#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
130pub struct ConditionalListJoinerPattern<'data> {
131    /// The default pattern
132    #[cfg_attr(feature = "serde", serde(borrow))]
133    pub default: ListJoinerPattern<'data>,
134    /// And optional special case
135    #[cfg_attr(
136        feature = "serde",
137        serde(borrow, deserialize_with = "SpecialCasePattern::deserialize_option")
138    )]
139    pub special_case: Option<SpecialCasePattern<'data>>,
140}
141
142/// The special case of a [`ConditionalListJoinerPattern`]
143///
144/// <div class="stab unstable">
145/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
146/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
147/// to be stable, their Rust representation might not be. Use with caution.
148/// </div>
149#[derive(Clone, Debug, PartialEq, yoke::Yokeable, zerofrom::ZeroFrom)]
150#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
151#[cfg_attr(feature = "datagen", databake(path = icu_list::provider))]
152pub struct SpecialCasePattern<'data> {
153    /// The condition on the following element
154    pub condition: SerdeDFA<'data>,
155    /// The pattern if the condition matches
156    pub pattern: ListJoinerPattern<'data>,
157}
158
159#[cfg(feature = "serde")]
160impl<'data> SpecialCasePattern<'data> {
161    // If the condition doesn't deserialize, the whole special case becomes `None`
162    fn deserialize_option<'de: 'data, D>(deserializer: D) -> Result<Option<Self>, D::Error>
163    where
164        D: serde::de::Deserializer<'de>,
165    {
166        use serde::Deserialize;
167
168        #[derive(Deserialize)]
169        struct SpecialCasePatternOptionalDfa<'data> {
170            #[cfg_attr(
171                feature = "serde",
172                serde(borrow, deserialize_with = "SerdeDFA::maybe_deserialize")
173            )]
174            pub condition: Option<SerdeDFA<'data>>,
175            #[cfg_attr(feature = "serde", serde(borrow))]
176            pub pattern: ListJoinerPattern<'data>,
177        }
178
179        Ok(
180            match Option::<SpecialCasePatternOptionalDfa<'data>>::deserialize(deserializer)? {
181                Some(SpecialCasePatternOptionalDfa {
182                    condition: Some(condition),
183                    pattern,
184                }) => Some(SpecialCasePattern { condition, pattern }),
185                _ => None,
186            },
187        )
188    }
189}
190
191/// A pattern containing two numeric placeholders ("{0}, and {1}.")
192///
193/// <div class="stab unstable">
194/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
195/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
196/// to be stable, their Rust representation might not be. Use with caution.
197/// </div>
198#[derive(Clone, Debug, PartialEq, yoke::Yokeable, zerofrom::ZeroFrom)]
199#[cfg_attr(feature = "datagen", derive(serde::Serialize))]
200pub struct ListJoinerPattern<'data> {
201    /// The pattern string without the placeholders
202    pub(crate) string: VarZeroCow<'data, str>,
203    /// The index of the first placeholder. Always <= index_1.
204    // Always 0 for CLDR data, so we don't need to serialize it.
205    // In-memory we have free space for it as index_1 doesn't
206    // fill a word.
207    #[cfg_attr(feature = "datagen", serde(skip))]
208    pub(crate) index_0: u8,
209    /// The index of the second placeholder. Always < string.len().
210    pub(crate) index_1: u8,
211}
212
213#[cfg(feature = "serde")]
214impl<'de: 'data, 'data> serde::Deserialize<'de> for ListJoinerPattern<'data> {
215    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
216    where
217        D: serde::Deserializer<'de>,
218    {
219        #[derive(serde::Deserialize)]
220        struct Dummy<'data> {
221            #[cfg_attr(feature = "serde", serde(borrow))]
222            string: VarZeroCow<'data, str>,
223            index_1: u8,
224        }
225        let Dummy { string, index_1 } = Dummy::deserialize(deserializer)?;
226
227        if index_1 as usize > string.len() {
228            use serde::de::Error;
229            Err(D::Error::custom("invalid index_1"))
230        } else {
231            Ok(ListJoinerPattern {
232                string,
233                index_0: 0,
234                index_1,
235            })
236        }
237    }
238}
239
240impl<'a> ListJoinerPattern<'a> {
241    /// Constructs a [`ListJoinerPattern`] from raw parts. Used by databake.
242    ///
243    /// # Panics
244    /// If `string[..index_1]` panics.
245    pub const fn from_parts(string: VarZeroCow<'a, str>, index_1: u8) -> Self {
246        Self {
247            string,
248            index_0: 0,
249            index_1,
250        }
251    }
252}
253
254#[cfg(feature = "datagen")]
255impl databake::Bake for ListJoinerPattern<'_> {
256    fn bake(&self, env: &databake::CrateEnv) -> databake::TokenStream {
257        env.insert("icu_list");
258        let string = self.string.bake(env);
259        let index_1 = self.index_1.bake(env);
260        databake::quote! {
261            icu_list::provider::ListJoinerPattern::from_parts(#string, #index_1)
262        }
263    }
264}
265
266#[cfg(feature = "datagen")]
267impl databake::BakeSize for ListJoinerPattern<'_> {
268    fn borrows_size(&self) -> usize {
269        self.string.borrows_size()
270    }
271}
272
273#[cfg(all(test, feature = "datagen"))]
274#[test]
275fn databake() {
276    databake::test_bake!(
277        ListJoinerPattern,
278        const,
279        crate::provider::ListJoinerPattern::from_parts(
280            unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") },
281            2u8
282        ),
283        icu_list
284    );
285}