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