• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #[diplomat::bridge]
6 #[diplomat::abi_rename = "icu4x_{0}_mv1"]
7 #[diplomat::attr(auto, namespace = "icu4x")]
8 pub mod ffi {
9     use alloc::boxed::Box;
10 
11     #[cfg(any(feature = "compiled_data", feature = "buffer_provider"))]
12     use crate::locale_core::ffi::Locale;
13     #[cfg(feature = "buffer_provider")]
14     use crate::provider::ffi::DataProvider;
15     use crate::{errors::ffi::DataError, fixed_decimal::ffi::Decimal};
16     use icu_decimal::options::DecimalFormatterOptions;
17     #[cfg(any(feature = "compiled_data", feature = "buffer_provider"))]
18     use icu_decimal::DecimalFormatterPreferences;
19 
20     use writeable::Writeable;
21 
22     #[diplomat::opaque]
23     /// An ICU4X Decimal Format object, capable of formatting a [`Decimal`] as a string.
24     #[diplomat::rust_link(icu::decimal::DecimalFormatter, Struct)]
25     #[diplomat::rust_link(icu::datetime::FormattedDecimal, Struct, hidden)]
26     pub struct DecimalFormatter(pub icu_decimal::DecimalFormatter);
27 
28     #[diplomat::rust_link(icu::decimal::options::GroupingStrategy, Enum)]
29     #[diplomat::enum_convert(icu_decimal::options::GroupingStrategy, needs_wildcard)]
30     pub enum DecimalGroupingStrategy {
31         Auto,
32         Never,
33         Always,
34         Min2,
35     }
36 
37     impl DecimalFormatter {
38         /// Creates a new [`DecimalFormatter`], using compiled data
39         #[diplomat::rust_link(icu::decimal::DecimalFormatter::try_new, FnInStruct)]
40         #[diplomat::attr(all(supports = fallible_constructors, supports = named_constructors), named_constructor = "with_grouping_strategy")]
41         #[diplomat::demo(default_constructor)]
42         #[cfg(feature = "compiled_data")]
create_with_grouping_strategy( locale: &Locale, grouping_strategy: Option<DecimalGroupingStrategy>, ) -> Result<Box<DecimalFormatter>, DataError>43         pub fn create_with_grouping_strategy(
44             locale: &Locale,
45             grouping_strategy: Option<DecimalGroupingStrategy>,
46         ) -> Result<Box<DecimalFormatter>, DataError> {
47             let prefs = DecimalFormatterPreferences::from(&locale.0);
48 
49             let mut options = DecimalFormatterOptions::default();
50             options.grouping_strategy = grouping_strategy.map(Into::into);
51             Ok(Box::new(DecimalFormatter(
52                 icu_decimal::DecimalFormatter::try_new(prefs, options)?,
53             )))
54         }
55 
56         /// Creates a new [`DecimalFormatter`], using a particular data source.
57         #[diplomat::rust_link(icu::decimal::DecimalFormatter::try_new, FnInStruct)]
58         #[diplomat::attr(all(supports = fallible_constructors, supports = named_constructors), named_constructor = "with_grouping_strategy_and_provider")]
59         #[diplomat::demo(default_constructor)]
60         #[cfg(feature = "buffer_provider")]
create_with_grouping_strategy_and_provider( provider: &DataProvider, locale: &Locale, grouping_strategy: Option<DecimalGroupingStrategy>, ) -> Result<Box<DecimalFormatter>, DataError>61         pub fn create_with_grouping_strategy_and_provider(
62             provider: &DataProvider,
63             locale: &Locale,
64             grouping_strategy: Option<DecimalGroupingStrategy>,
65         ) -> Result<Box<DecimalFormatter>, DataError> {
66             let prefs = DecimalFormatterPreferences::from(&locale.0);
67 
68             let mut options = DecimalFormatterOptions::default();
69             options.grouping_strategy = grouping_strategy.map(Into::into);
70             Ok(Box::new(DecimalFormatter(
71                 icu_decimal::DecimalFormatter::try_new_with_buffer_provider(
72                     provider.get()?,
73                     prefs,
74                     options,
75                 )?,
76             )))
77         }
78 
79         /// Creates a new [`DecimalFormatter`] from preconstructed locale data.
80         #[diplomat::rust_link(icu::decimal::provider::DecimalSymbolsV2, Struct)]
81         #[allow(clippy::too_many_arguments)]
create_with_manual_data( plus_sign_prefix: &DiplomatStr, plus_sign_suffix: &DiplomatStr, minus_sign_prefix: &DiplomatStr, minus_sign_suffix: &DiplomatStr, decimal_separator: &DiplomatStr, grouping_separator: &DiplomatStr, primary_group_size: u8, secondary_group_size: u8, min_group_size: u8, digits: &[DiplomatChar], grouping_strategy: Option<DecimalGroupingStrategy>, ) -> Result<Box<DecimalFormatter>, DataError>82         pub fn create_with_manual_data(
83             plus_sign_prefix: &DiplomatStr,
84             plus_sign_suffix: &DiplomatStr,
85             minus_sign_prefix: &DiplomatStr,
86             minus_sign_suffix: &DiplomatStr,
87             decimal_separator: &DiplomatStr,
88             grouping_separator: &DiplomatStr,
89             primary_group_size: u8,
90             secondary_group_size: u8,
91             min_group_size: u8,
92             digits: &[DiplomatChar],
93             grouping_strategy: Option<DecimalGroupingStrategy>,
94         ) -> Result<Box<DecimalFormatter>, DataError> {
95             use core::cell::RefCell;
96             use icu_provider::prelude::*;
97             use zerovec::VarZeroCow;
98 
99             fn str_to_cow(s: &'_ diplomat_runtime::DiplomatStr) -> VarZeroCow<'_, str> {
100                 if let Ok(s) = core::str::from_utf8(s) {
101                     VarZeroCow::new_borrowed(s)
102                 } else {
103                     VarZeroCow::new_owned(
104                         alloc::string::String::from_utf8_lossy(s)
105                             .into_owned()
106                             .into_boxed_str(),
107                     )
108                 }
109             }
110 
111             use icu_decimal::provider::{
112                 DecimalDigitsV1, DecimalSymbolStrsBuilder, DecimalSymbols, DecimalSymbolsV2,
113                 GroupingSizes,
114             };
115             let mut new_digits = ['\0'; 10];
116             for (old, new) in digits
117                 .iter()
118                 .copied()
119                 .chain(core::iter::repeat(char::REPLACEMENT_CHARACTER as u32))
120                 .zip(new_digits.iter_mut())
121             {
122                 *new = char::from_u32(old).unwrap_or(char::REPLACEMENT_CHARACTER);
123             }
124             let digits = new_digits;
125             let strings = DecimalSymbolStrsBuilder {
126                 plus_sign_prefix: str_to_cow(plus_sign_prefix),
127                 plus_sign_suffix: str_to_cow(plus_sign_suffix),
128                 minus_sign_prefix: str_to_cow(minus_sign_prefix),
129                 minus_sign_suffix: str_to_cow(minus_sign_suffix),
130                 decimal_separator: str_to_cow(decimal_separator),
131                 grouping_separator: str_to_cow(grouping_separator),
132                 numsys: "zyyy".into(),
133             };
134 
135             let grouping_sizes = GroupingSizes {
136                 primary: primary_group_size,
137                 secondary: secondary_group_size,
138                 min_grouping: min_group_size,
139             };
140 
141             let mut options = DecimalFormatterOptions::default();
142             options.grouping_strategy = grouping_strategy.map(Into::into);
143 
144             struct Provider(RefCell<Option<DecimalSymbols<'static>>>, [char; 10]);
145             impl DataProvider<DecimalSymbolsV2> for Provider {
146                 fn load(
147                     &self,
148                     _req: icu_provider::DataRequest,
149                 ) -> Result<icu_provider::DataResponse<DecimalSymbolsV2>, icu_provider::DataError>
150                 {
151                     Ok(DataResponse {
152                         metadata: Default::default(),
153                         payload: DataPayload::from_owned(
154                             self.0
155                                 .borrow_mut()
156                                 .take()
157                                 // We only have one payload
158                                 .ok_or(DataErrorKind::Custom.into_error())?,
159                         ),
160                     })
161                 }
162             }
163             impl DataProvider<DecimalDigitsV1> for Provider {
164                 fn load(
165                     &self,
166                     _req: icu_provider::DataRequest,
167                 ) -> Result<icu_provider::DataResponse<DecimalDigitsV1>, icu_provider::DataError>
168                 {
169                     Ok(DataResponse {
170                         metadata: Default::default(),
171                         payload: DataPayload::from_owned(self.1),
172                     })
173                 }
174             }
175             let provider = Provider(
176                 RefCell::new(Some(DecimalSymbols {
177                     strings: VarZeroCow::from_encodeable(&strings),
178                     grouping_sizes,
179                 })),
180                 digits,
181             );
182             Ok(Box::new(DecimalFormatter(
183                 icu_decimal::DecimalFormatter::try_new_unstable(
184                     &provider,
185                     Default::default(),
186                     options,
187                 )?,
188             )))
189         }
190 
191         /// Formats a [`Decimal`] to a string.
192         #[diplomat::rust_link(icu::decimal::DecimalFormatter::format, FnInStruct)]
193         #[diplomat::rust_link(icu::decimal::DecimalFormatter::format_to_string, FnInStruct, hidden)]
194         #[diplomat::rust_link(icu::decimal::FormattedDecimal, Struct, hidden)]
195         #[diplomat::rust_link(icu::decimal::FormattedDecimal::write_to, FnInStruct, hidden)]
196         #[diplomat::rust_link(icu::decimal::FormattedDecimal::to_string, FnInStruct, hidden)]
format(&self, value: &Decimal, write: &mut diplomat_runtime::DiplomatWrite)197         pub fn format(&self, value: &Decimal, write: &mut diplomat_runtime::DiplomatWrite) {
198             let _infallible = self.0.format(&value.0).write_to(write);
199         }
200     }
201 }
202