fluent_static_function/
builtins.rs1use std::str::FromStr;
2
3use fluent_static_value::{
4 number::format::{CurrencyDisplayStyle, GroupingStyle, UnitDisplayStyle},
5 Number, NumberFormat, Value,
6};
7
8pub fn number<'a, 'b>(
9 positional_args: &'a [Value<'a>],
10 named_args: &'a [(&'a str, Value<'a>)],
11) -> Value<'b> {
12 if let Some(value) = positional_args.get(0) {
13 match value {
14 Value::String(s) => Number::from_str(s)
15 .map(|n| Value::Number {
16 value: n,
17 format: Some(parse_number_format(None, named_args)),
18 })
19 .unwrap_or(Value::Error),
20 Value::Number { value, format } => Value::Number {
21 value: value.clone(),
22 format: Some(parse_number_format(format.clone(), named_args)),
23 },
24 Value::Empty => Value::Empty,
25 Value::Error => Value::Error,
26 }
27 } else {
28 Value::Error
29 }
30}
31
32fn parse_number_format<'a>(
33 value_format: Option<NumberFormat>,
34 named_args: &'a [(&'a str, Value<'a>)],
35) -> NumberFormat {
36 let mut result = value_format.unwrap_or_default();
37 for (key, value) in named_args {
38 match *key {
39 "currencyDisplay" if value.is_string() && result.style.is_currency() => {
40 if let Value::String(s) = value {
41 result.style.set_currency_display_style(
42 CurrencyDisplayStyle::from_str(s).unwrap_or_default(),
43 );
44 }
45 }
46 "unitDisplay" if value.is_string() && result.style.is_unit() => {
47 if let Value::String(s) = value {
48 result
49 .style
50 .set_unit_display_style(UnitDisplayStyle::from_str(s).unwrap_or_default());
51 }
52 }
53 "useGrouping" if value.is_string() => {
54 if let Value::String(s) = value {
55 result.use_grouping = GroupingStyle::from_str(s).unwrap_or_default();
56 }
57 }
58 "minimumIntegerDigits" => result.minimum_integer_digits = read_digits(value, 1, 21),
59 "minimumFractionDigits" => result.minimum_fraction_digits = read_digits(value, 0, 100),
60 "maximumFractionDigits" => result.maximum_fraction_digits = read_digits(value, 0, 100),
61 "minimumSignificantDigits" => {
62 result.minimum_significant_digits = read_digits(value, 1, 21)
63 }
64 "maximumSignificantDigits" => {
65 result.maximum_significant_digits = read_digits(value, 1, 21)
66 }
67 _ => {}
68 }
69 }
70 result
71}
72
73fn read_digits<'a>(value: &Value<'a>, min: usize, max: usize) -> Option<usize> {
74 match value {
75 Value::String(s) => Number::from_str(s).ok().map(|n| clamp(&n, min, max)),
76 Value::Number { value, .. } => Some(clamp(value, min, max)),
77 _ => None,
78 }
79}
80
81fn clamp(value: &Number, min: usize, max: usize) -> usize {
82 match value {
83 Number::I64(val) => (*val).clamp(min as i64, max as i64) as usize,
84 Number::U64(val) => (*val).clamp(min as u64, max as u64) as usize,
85 Number::I128(val) => (*val).clamp(min as i128, max as i128) as usize,
86 Number::U128(val) => (*val).clamp(min as u128, max as u128) as usize,
87 Number::F64(val) => {
88 let clamped = val.clamp(min as f64, max as f64);
89 clamped.round() as usize
90 }
91 }
92}