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