icu_decimal/provider.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//! 🚧 \[Unstable\] Data provider struct definitions for this ICU4X component.
6//!
7//! <div class="stab unstable">
8//! 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
9//! including in SemVer minor releases. While the serde representation of data structs is guaranteed
10//! to be stable, their Rust representation might not be. Use with caution.
11//! </div>
12//!
13//! Read more about data providers: [`icu_provider`]
14//!
15//! # Examples
16//!
17//! ## Get the resolved numbering system
18//!
19//! In a constructor call, the _last_ request for [`DecimalDigitsV1`]
20//! contains the resolved numbering system as its attribute:
21//!
22//! ```
23//! use icu::decimal::provider::DecimalDigitsV1;
24//! use icu::decimal::DecimalFormatter;
25//! use icu::locale::locale;
26//! use icu_provider::prelude::*;
27//! use std::any::TypeId;
28//! use std::cell::RefCell;
29//!
30//! struct NumberingSystemInspectionProvider<P> {
31//! inner: P,
32//! numbering_system: RefCell<Option<Box<DataMarkerAttributes>>>,
33//! }
34//!
35//! impl<M, P> DataProvider<M> for NumberingSystemInspectionProvider<P>
36//! where
37//! M: DataMarker,
38//! P: DataProvider<M>,
39//! {
40//! fn load(&self, req: DataRequest) -> Result<DataResponse<M>, DataError> {
41//! if TypeId::of::<M>() == TypeId::of::<DecimalDigitsV1>() {
42//! *self.numbering_system.try_borrow_mut().unwrap() =
43//! Some(req.id.marker_attributes.to_owned());
44//! }
45//! self.inner.load(req)
46//! }
47//! }
48//!
49//! let provider = NumberingSystemInspectionProvider {
50//! inner: icu::decimal::provider::Baked,
51//! numbering_system: RefCell::new(None),
52//! };
53//!
54//! let formatter = DecimalFormatter::try_new_unstable(
55//! &provider,
56//! locale!("th").into(),
57//! Default::default(),
58//! )
59//! .unwrap();
60//!
61//! assert_eq!(
62//! provider
63//! .numbering_system
64//! .borrow()
65//! .as_ref()
66//! .map(|x| x.as_str()),
67//! Some("latn")
68//! );
69//!
70//! let formatter = DecimalFormatter::try_new_unstable(
71//! &provider,
72//! locale!("th-u-nu-thai").into(),
73//! Default::default(),
74//! )
75//! .unwrap();
76//!
77//! assert_eq!(
78//! provider
79//! .numbering_system
80//! .borrow()
81//! .as_ref()
82//! .map(|x| x.as_str()),
83//! Some("thai")
84//! );
85//!
86//! let formatter = DecimalFormatter::try_new_unstable(
87//! &provider,
88//! locale!("th-u-nu-adlm").into(),
89//! Default::default(),
90//! )
91//! .unwrap();
92//!
93//! assert_eq!(
94//! provider
95//! .numbering_system
96//! .borrow()
97//! .as_ref()
98//! .map(|x| x.as_str()),
99//! Some("adlm")
100//! );
101//! ```
102
103// Provider structs must be stable
104#![allow(clippy::exhaustive_structs)]
105#![allow(clippy::exhaustive_enums)]
106
107use icu_provider::prelude::*;
108use zerovec::VarZeroCow;
109
110#[cfg(feature = "compiled_data")]
111#[derive(Debug)]
112/// Baked data
113///
114/// <div class="stab unstable">
115/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
116/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
117/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
118/// </div>
119pub struct Baked;
120
121#[cfg(feature = "compiled_data")]
122#[allow(unused_imports)]
123const _: () = {
124 use icu_decimal_data::*;
125 pub mod icu {
126 pub use crate as decimal;
127 pub use icu_locale as locale;
128 }
129 make_provider!(Baked);
130 impl_decimal_symbols_v1!(Baked);
131 impl_decimal_digits_v1!(Baked);
132};
133
134icu_provider::data_marker!(
135 /// Data marker for decimal symbols
136 DecimalSymbolsV1,
137 "decimal/symbols/v1",
138 DecimalSymbols<'static>,
139);
140
141icu_provider::data_marker!(
142 /// The digits for a given numbering system. This data ought to be stored in the `und` locale with an auxiliary key
143 /// set to the numbering system code.
144 DecimalDigitsV1,
145 "decimal/digits/v1",
146 [char; 10],
147 #[cfg(feature = "datagen")]
148 attributes_domain = "numbering_system"
149);
150
151#[cfg(feature = "datagen")]
152/// The latest minimum set of markers required by this component.
153pub const MARKERS: &[DataMarkerInfo] = &[DecimalSymbolsV1::INFO, DecimalDigitsV1::INFO];
154
155/// A collection of settings expressing where to put grouping separators in a decimal number.
156/// For example, `1,000,000` has two grouping separators, positioned along every 3 digits.
157///
158/// <div class="stab unstable">
159/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
160/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
161/// to be stable, their Rust representation might not be. Use with caution.
162/// </div>
163#[derive(Debug, PartialEq, Clone, yoke::Yokeable, Copy, zerofrom::ZeroFrom)]
164#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
165#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
166#[cfg_attr(feature = "datagen", databake(path = icu_decimal::provider))]
167pub struct GroupingSizes {
168 /// The size of the first (lowest-magnitude) group.
169 ///
170 /// If 0, grouping separators will never be shown.
171 pub primary: u8,
172
173 /// The size of groups after the first group.
174 ///
175 /// If 0, defaults to be the same as `primary`.
176 pub secondary: u8,
177
178 /// The minimum number of digits required before the first group. For example, if `primary=3`
179 /// and `min_grouping=2`, grouping separators will be present on 10,000 and above.
180 pub min_grouping: u8,
181}
182
183/// A stack representation of the strings used in [`DecimalSymbols`], i.e. a builder type
184/// for [`DecimalSymbolsStrs`]. This type can be obtained from a [`DecimalSymbolsStrs`]
185/// the `From`/`Into` traits.
186///
187/// <div class="stab unstable">
188/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
189/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
190/// to be stable, their Rust representation might not be. Use with caution.
191/// </div>
192#[derive(Debug, PartialEq, Clone, yoke::Yokeable, zerofrom::ZeroFrom)]
193#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
194#[cfg_attr(feature = "datagen", derive(serde::Serialize))]
195#[zerovec::make_varule(DecimalSymbolsStrs)]
196#[zerovec::derive(Debug)]
197#[zerovec::skip_derive(Ord)]
198#[cfg_attr(not(feature = "alloc"), zerovec::skip_derive(ZeroMapKV, ToOwned))]
199#[cfg_attr(feature = "serde", zerovec::derive(Deserialize))]
200#[cfg_attr(feature = "datagen", zerovec::derive(Serialize))]
201// Each affix/separator is at most three characters, which tends to be around 3-12 bytes each
202// and the numbering system is at most 8 ascii bytes, All put together the indexing is extremely
203// unlikely to have to go past 256.
204#[zerovec::format(zerovec::vecs::Index8)]
205pub struct DecimalSymbolStrsBuilder<'data> {
206 /// Prefix to apply when a negative sign is needed.
207 #[cfg_attr(feature = "serde", serde(borrow))]
208 pub minus_sign_prefix: VarZeroCow<'data, str>,
209 /// Suffix to apply when a negative sign is needed.
210 #[cfg_attr(feature = "serde", serde(borrow))]
211 pub minus_sign_suffix: VarZeroCow<'data, str>,
212
213 /// Prefix to apply when a positive sign is needed.
214 #[cfg_attr(feature = "serde", serde(borrow))]
215 pub plus_sign_prefix: VarZeroCow<'data, str>,
216 /// Suffix to apply when a positive sign is needed.
217 #[cfg_attr(feature = "serde", serde(borrow))]
218 pub plus_sign_suffix: VarZeroCow<'data, str>,
219
220 /// Character used to separate the integer and fraction parts of the number.
221 #[cfg_attr(feature = "serde", serde(borrow))]
222 pub decimal_separator: VarZeroCow<'data, str>,
223
224 /// Character used to separate groups in the integer part of the number.
225 #[cfg_attr(feature = "serde", serde(borrow))]
226 pub grouping_separator: VarZeroCow<'data, str>,
227
228 /// The numbering system to use.
229 #[cfg_attr(feature = "serde", serde(borrow))]
230 pub numsys: VarZeroCow<'data, str>,
231}
232
233#[cfg(feature = "alloc")]
234impl DecimalSymbolStrsBuilder<'_> {
235 /// Build a [`DecimalSymbolsStrs`]
236 pub fn build(&self) -> VarZeroCow<'static, DecimalSymbolsStrs> {
237 VarZeroCow::from_encodeable(self)
238 }
239}
240
241/// Symbols and metadata required for formatting a [`Decimal`](crate::Decimal).
242///
243/// <div class="stab unstable">
244/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
245/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
246/// to be stable, their Rust representation might not be. Use with caution.
247/// </div>
248#[derive(Debug, PartialEq, Clone, yoke::Yokeable, zerofrom::ZeroFrom)]
249#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
250#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
251#[cfg_attr(feature = "datagen", databake(path = icu_decimal::provider))]
252pub struct DecimalSymbols<'data> {
253 /// String data for the symbols: +/- affixes and separators
254 #[cfg_attr(feature = "serde", serde(borrow))]
255 // We use a VarZeroCow here to reduce the stack size of DecimalSymbols: instead of serializing multiple strs,
256 // this type will now serialize as a single u8 buffer with optimized indexing that packs all the data together
257 pub strings: VarZeroCow<'data, DecimalSymbolsStrs>,
258
259 /// Settings used to determine where to place groups in the integer part of the number.
260 pub grouping_sizes: GroupingSizes,
261}
262
263icu_provider::data_struct!(
264 DecimalSymbols<'_>,
265 #[cfg(feature = "datagen")]
266);
267
268impl DecimalSymbols<'_> {
269 /// Return (prefix, suffix) for the minus sign
270 pub fn minus_sign_affixes(&self) -> (&str, &str) {
271 (
272 self.strings.minus_sign_prefix(),
273 self.strings.minus_sign_suffix(),
274 )
275 }
276 /// Return (prefix, suffix) for the minus sign
277 pub fn plus_sign_affixes(&self) -> (&str, &str) {
278 (
279 self.strings.plus_sign_prefix(),
280 self.strings.plus_sign_suffix(),
281 )
282 }
283 /// Return thhe decimal separator
284 pub fn decimal_separator(&self) -> &str {
285 self.strings.decimal_separator()
286 }
287 /// Return thhe decimal separator
288 pub fn grouping_separator(&self) -> &str {
289 self.strings.grouping_separator()
290 }
291
292 /// Return the numbering system
293 pub fn numsys(&self) -> &str {
294 self.strings.numsys()
295 }
296}
297
298impl DecimalSymbols<'static> {
299 /// Create a new en-US format for use in testing
300 #[cfg(feature = "datagen")]
301 pub fn new_en_for_testing() -> Self {
302 let strings = DecimalSymbolStrsBuilder {
303 minus_sign_prefix: VarZeroCow::new_borrowed("-"),
304 minus_sign_suffix: VarZeroCow::new_borrowed(""),
305 plus_sign_prefix: VarZeroCow::new_borrowed("+"),
306 plus_sign_suffix: VarZeroCow::new_borrowed(""),
307 decimal_separator: VarZeroCow::new_borrowed("."),
308 grouping_separator: VarZeroCow::new_borrowed(","),
309 numsys: VarZeroCow::new_borrowed("latn"),
310 };
311 Self {
312 strings: VarZeroCow::from_encodeable(&strings),
313 grouping_sizes: GroupingSizes {
314 primary: 3,
315 secondary: 3,
316 min_grouping: 1,
317 },
318 }
319 }
320}