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}