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