@@ -3,9 +3,19 @@ use std::convert::TryInto;
3
3
use std:: default:: Default ;
4
4
use std:: str:: FromStr ;
5
5
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 ;
6
13
use intl_pluralrules:: operands:: PluralOperands ;
14
+ use unic_langid:: LanguageIdentifier ;
7
15
8
16
use crate :: args:: FluentArgs ;
17
+ use crate :: bundle:: { FluentBundle , IcuDataProvider } ;
18
+ use crate :: memoizer:: MemoizerKind ;
9
19
use crate :: types:: FluentValue ;
10
20
11
21
#[ derive( Debug , Default , Copy , Clone , Hash , PartialEq , Eq ) ]
@@ -145,22 +155,38 @@ impl FluentNumber {
145
155
Self { value, options }
146
156
}
147
157
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
+
150
182
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 ) ) ;
162
184
}
163
- val. into ( )
185
+ fixed_decimal
186
+ }
187
+
188
+ pub fn as_string_basic ( & self ) -> String {
189
+ self . as_fixed_decimal ( ) . to_string ( )
164
190
}
165
191
}
166
192
@@ -250,8 +276,59 @@ from_num!(i8 i16 i32 i64 i128 isize);
250
276
from_num ! ( u8 u16 u32 u64 u128 usize ) ;
251
277
from_num ! ( f32 f64 ) ;
252
278
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
+
253
327
#[ cfg( test) ]
254
328
mod tests {
329
+ use super :: to_icu_lang_id;
330
+ use unic_langid:: langid;
331
+
255
332
use crate :: types:: FluentValue ;
256
333
257
334
#[ test]
@@ -261,4 +338,16 @@ mod tests {
261
338
let z: FluentValue = y. into ( ) ;
262
339
assert_eq ! ( z, FluentValue :: try_number( "1" ) ) ;
263
340
}
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
+ }
264
353
}
0 commit comments