@@ -3,9 +3,19 @@ use std::convert::TryInto;
33use std:: default:: Default ;
44use std:: str:: FromStr ;
55
6+ use fixed_decimal:: { DoublePrecision , FixedDecimal } ;
7+ use icu_decimal:: options:: { FixedDecimalFormatterOptions , GroupingStrategy } ;
8+ use icu_decimal:: provider:: DecimalSymbolsV1Marker ;
9+ use icu_decimal:: { DecimalError , FixedDecimalFormatter } ;
10+ use icu_locid:: { LanguageIdentifier as IcuLanguageIdentifier , ParserError } ;
11+ use icu_provider:: prelude:: * ;
12+ use intl_memoizer:: Memoizable ;
613use intl_pluralrules:: operands:: PluralOperands ;
14+ use unic_langid:: LanguageIdentifier ;
715
816use crate :: args:: FluentArgs ;
17+ use crate :: bundle:: { FluentBundle , IcuDataProvider } ;
18+ use crate :: memoizer:: MemoizerKind ;
919use crate :: types:: FluentValue ;
1020
1121#[ derive( Debug , Copy , Clone , Hash , PartialEq , Eq ) ]
@@ -133,22 +143,38 @@ impl FluentNumber {
133143 Self { value, options }
134144 }
135145
136- pub fn as_string ( & self ) -> Cow < ' static , str > {
137- let mut val = self . value . to_string ( ) ;
146+ pub fn as_string < R , M : MemoizerKind > ( & self , bundle : & FluentBundle < R , M > ) -> Cow < ' static , str > {
147+ let fixed_decimal = self . as_fixed_decimal ( ) ;
148+ let options = FormatterOptions {
149+ use_grouping : self . options . use_grouping ,
150+ } ;
151+ if let Some ( data_provider) = & bundle. icu_data_provider {
152+ let formatted = bundle
153+ . intls
154+ . with_try_get_threadsafe :: < NumberFormatter , _ , _ > (
155+ ( options, ) ,
156+ data_provider,
157+ |formatter| formatter. 0 . format_to_string ( & fixed_decimal) ,
158+ )
159+ . unwrap ( ) ;
160+ return formatted. into ( ) ;
161+ }
162+
163+ fixed_decimal. to_string ( ) . into ( )
164+ }
165+
166+ fn as_fixed_decimal ( & self ) -> FixedDecimal {
167+ let mut fixed_decimal =
168+ FixedDecimal :: try_from_f64 ( self . value , DoublePrecision :: Floating ) . unwrap ( ) ;
169+
138170 if let Some ( minfd) = self . options . minimum_fraction_digits {
139- if let Some ( pos) = val. find ( '.' ) {
140- let frac_num = val. len ( ) - pos - 1 ;
141- let missing = if frac_num > minfd {
142- 0
143- } else {
144- minfd - frac_num
145- } ;
146- val = format ! ( "{}{}" , val, "0" . repeat( missing) ) ;
147- } else {
148- val = format ! ( "{}.{}" , val, "0" . repeat( minfd) ) ;
149- }
171+ fixed_decimal. pad_end ( -( minfd as i16 ) ) ;
150172 }
151- val. into ( )
173+ fixed_decimal
174+ }
175+
176+ pub fn as_string_basic ( & self ) -> String {
177+ self . as_fixed_decimal ( ) . to_string ( )
152178 }
153179}
154180
@@ -238,8 +264,58 @@ from_num!(i8 i16 i32 i64 i128 isize);
238264from_num ! ( u8 u16 u32 u64 u128 usize ) ;
239265from_num ! ( f32 f64 ) ;
240266
267+ pub type NumberFormatProvider = Box < dyn DataProvider < DecimalSymbolsV1Marker > > ;
268+
269+ #[ derive( Debug , Eq , PartialEq , Clone , Default , Hash ) ]
270+ struct FormatterOptions {
271+ use_grouping : bool ,
272+ }
273+
274+ struct NumberFormatter ( FixedDecimalFormatter ) ;
275+
276+ #[ derive( Debug ) ]
277+ enum NumberFormatterError {
278+ ParserError ( ParserError ) ,
279+ DecimalError ( DecimalError ) ,
280+ }
281+
282+ impl Memoizable for NumberFormatter {
283+ type Args = ( FormatterOptions , ) ;
284+ type Error = NumberFormatterError ;
285+ type DataProvider = IcuDataProvider ;
286+
287+ fn construct (
288+ lang : LanguageIdentifier ,
289+ args : Self :: Args ,
290+ data_provider : & Self :: DataProvider ,
291+ ) -> Result < Self , Self :: Error > {
292+ let locale = to_icu_lang_id ( lang) . map_err ( NumberFormatterError :: ParserError ) ?;
293+
294+ let mut options: FixedDecimalFormatterOptions = Default :: default ( ) ;
295+ options. grouping_strategy = match args. 0 . use_grouping {
296+ true => GroupingStrategy :: Always ,
297+ false => GroupingStrategy :: Auto ,
298+ } ;
299+
300+ let inner = FixedDecimalFormatter :: try_new_with_any_provider (
301+ data_provider,
302+ & locale. into ( ) ,
303+ options,
304+ )
305+ . map_err ( NumberFormatterError :: DecimalError ) ?;
306+ Ok ( NumberFormatter ( inner) )
307+ }
308+ }
309+
310+ fn to_icu_lang_id ( lang : LanguageIdentifier ) -> Result < IcuLanguageIdentifier , ParserError > {
311+ return IcuLanguageIdentifier :: try_from_locale_bytes ( lang. to_string ( ) . as_bytes ( ) ) ;
312+ }
313+
241314#[ cfg( test) ]
242315mod tests {
316+ use super :: to_icu_lang_id;
317+ use unic_langid:: langid;
318+
243319 use crate :: types:: FluentValue ;
244320
245321 #[ test]
@@ -249,4 +325,16 @@ mod tests {
249325 let z: FluentValue = y. into ( ) ;
250326 assert_eq ! ( z, FluentValue :: try_number( 1 ) ) ;
251327 }
328+
329+ #[ test]
330+ fn lang_to_icu ( ) {
331+ assert_eq ! (
332+ to_icu_lang_id( langid!( "en-US" ) ) . unwrap( ) ,
333+ icu_locid:: langid!( "en-US" )
334+ ) ;
335+ assert_eq ! (
336+ to_icu_lang_id( langid!( "pl" ) ) . unwrap( ) ,
337+ icu_locid:: langid!( "pl" )
338+ ) ;
339+ }
252340}
0 commit comments