@@ -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 , Default , Copy , Clone , Hash , PartialEq , Eq ) ]
@@ -145,22 +155,38 @@ impl FluentNumber {
145155 Self { value, options }
146156 }
147157
148- pub fn as_string ( & self ) -> Cow < ' static , str > {
149- let mut val = self . value . to_string ( ) ;
158+ pub fn as_string < R , M : MemoizerKind > ( & self , bundle : & FluentBundle < R , M > ) -> Cow < ' static , str > {
159+ let fixed_decimal = self . as_fixed_decimal ( ) ;
160+ let options = FormatterOptions {
161+ use_grouping : self . options . use_grouping ,
162+ } ;
163+ if let Some ( data_provider) = & bundle. icu_data_provider {
164+ let formatted = bundle
165+ . intls
166+ . with_try_get_threadsafe :: < NumberFormatter , _ , _ > (
167+ ( options, ) ,
168+ data_provider,
169+ |formatter| formatter. 0 . format_to_string ( & fixed_decimal) ,
170+ )
171+ . unwrap ( ) ;
172+ return formatted. into ( ) ;
173+ }
174+
175+ fixed_decimal. to_string ( ) . into ( )
176+ }
177+
178+ fn as_fixed_decimal ( & self ) -> FixedDecimal {
179+ let mut fixed_decimal =
180+ FixedDecimal :: try_from_f64 ( self . value , DoublePrecision :: Floating ) . unwrap ( ) ;
181+
150182 if let Some ( minfd) = self . options . minimum_fraction_digits {
151- if let Some ( pos) = val. find ( '.' ) {
152- let frac_num = val. len ( ) - pos - 1 ;
153- let missing = if frac_num > minfd {
154- 0
155- } else {
156- minfd - frac_num
157- } ;
158- val = format ! ( "{}{}" , val, "0" . repeat( missing) ) ;
159- } else {
160- val = format ! ( "{}.{}" , val, "0" . repeat( minfd) ) ;
161- }
183+ fixed_decimal. pad_end ( -( minfd as i16 ) ) ;
162184 }
163- val. into ( )
185+ fixed_decimal
186+ }
187+
188+ pub fn as_string_basic ( & self ) -> String {
189+ self . as_fixed_decimal ( ) . to_string ( )
164190 }
165191}
166192
@@ -250,8 +276,59 @@ from_num!(i8 i16 i32 i64 i128 isize);
250276from_num ! ( u8 u16 u32 u64 u128 usize ) ;
251277from_num ! ( f32 f64 ) ;
252278
279+ pub type NumberFormatProvider = Box < dyn DataProvider < DecimalSymbolsV1Marker > > ;
280+
281+ #[ derive( Debug , Eq , PartialEq , Clone , Default , Hash ) ]
282+ struct FormatterOptions {
283+ use_grouping : bool ,
284+ }
285+
286+ struct NumberFormatter ( FixedDecimalFormatter ) ;
287+
288+ #[ derive( Debug ) ]
289+ #[ allow( dead_code) ]
290+ enum NumberFormatterError {
291+ ParserError ( ParserError ) ,
292+ DecimalError ( DecimalError ) ,
293+ }
294+
295+ impl Memoizable for NumberFormatter {
296+ type Args = ( FormatterOptions , ) ;
297+ type Error = NumberFormatterError ;
298+ type DataProvider = IcuDataProvider ;
299+
300+ fn construct (
301+ lang : LanguageIdentifier ,
302+ args : Self :: Args ,
303+ data_provider : & Self :: DataProvider ,
304+ ) -> Result < Self , Self :: Error > {
305+ let locale = to_icu_lang_id ( lang) . map_err ( NumberFormatterError :: ParserError ) ?;
306+
307+ let mut options: FixedDecimalFormatterOptions = Default :: default ( ) ;
308+ options. grouping_strategy = match args. 0 . use_grouping {
309+ true => GroupingStrategy :: Always ,
310+ false => GroupingStrategy :: Auto ,
311+ } ;
312+
313+ let inner = FixedDecimalFormatter :: try_new_with_any_provider (
314+ data_provider,
315+ & locale. into ( ) ,
316+ options,
317+ )
318+ . map_err ( NumberFormatterError :: DecimalError ) ?;
319+ Ok ( NumberFormatter ( inner) )
320+ }
321+ }
322+
323+ fn to_icu_lang_id ( lang : LanguageIdentifier ) -> Result < IcuLanguageIdentifier , ParserError > {
324+ return IcuLanguageIdentifier :: try_from_locale_bytes ( lang. to_string ( ) . as_bytes ( ) ) ;
325+ }
326+
253327#[ cfg( test) ]
254328mod tests {
329+ use super :: to_icu_lang_id;
330+ use unic_langid:: langid;
331+
255332 use crate :: types:: FluentValue ;
256333
257334 #[ test]
@@ -261,4 +338,16 @@ mod tests {
261338 let z: FluentValue = y. into ( ) ;
262339 assert_eq ! ( z, FluentValue :: try_number( "1" ) ) ;
263340 }
341+
342+ #[ test]
343+ fn lang_to_icu ( ) {
344+ assert_eq ! (
345+ to_icu_lang_id( langid!( "en-US" ) ) . unwrap( ) ,
346+ icu_locid:: langid!( "en-US" )
347+ ) ;
348+ assert_eq ! (
349+ to_icu_lang_id( langid!( "pl" ) ) . unwrap( ) ,
350+ icu_locid:: langid!( "pl" )
351+ ) ;
352+ }
264353}
0 commit comments