@@ -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:: locid:: { LanguageIdentifier as IcuLanguageIdentifier , ParserError } ;
8
+ use icu_decimal:: options:: { FixedDecimalFormatterOptions , GroupingStrategy } ;
9
+ use icu_decimal:: provider:: DecimalSymbolsV1Marker ;
10
+ use icu_decimal:: { DecimalError , FixedDecimalFormatter } ;
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 ;
18
+ use crate :: memoizer:: MemoizerKind ;
9
19
use crate :: types:: FluentValue ;
10
20
11
21
#[ derive( Debug , Copy , Clone , Hash , PartialEq , Eq ) ]
@@ -133,22 +143,38 @@ impl FluentNumber {
133
143
Self { value, options }
134
144
}
135
145
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. number_format_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
+
138
170
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 ) ) ;
150
172
}
151
- val. into ( )
173
+ fixed_decimal
174
+ }
175
+
176
+ pub fn as_string_basic ( & self ) -> String {
177
+ self . as_fixed_decimal ( ) . to_string ( )
152
178
}
153
179
}
154
180
@@ -238,8 +264,58 @@ from_num!(i8 i16 i32 i64 i128 isize);
238
264
from_num ! ( u8 u16 u32 u64 u128 usize ) ;
239
265
from_num ! ( f32 f64 ) ;
240
266
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 = NumberFormatProvider ;
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_unstable (
301
+ data_provider. as_ref ( ) ,
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
+
241
314
#[ cfg( test) ]
242
315
mod tests {
316
+ use super :: to_icu_lang_id;
317
+ use unic_langid:: langid;
318
+
243
319
use crate :: types:: FluentValue ;
244
320
245
321
#[ test]
@@ -249,4 +325,16 @@ mod tests {
249
325
let z: FluentValue = y. into ( ) ;
250
326
assert_eq ! ( z, FluentValue :: try_number( 1 ) ) ;
251
327
}
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
+ }
252
340
}
0 commit comments