Skip to main content

relibc/header/monetary/
mod.rs

1//! `monetary.h` implementation.
2//!
3//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/monetary.h.html>.
4
5// We should provide a strfmon() implementation that formats a monetary value,
6// according to the current locale (TODO).
7
8use alloc::string::{String, ToString};
9use core::str;
10
11use libm::{fabs, pow, round, trunc};
12
13extern crate alloc;
14
15mod strfmon;
16#[repr(C)]
17struct LocaleMonetaryInfo {
18    int_curr_symbol: &'static str,
19    currency_symbol: &'static str,
20    mon_decimal_point: &'static str,
21    mon_thousands_sep: &'static str,
22    mon_grouping: &'static [u8],
23    positive_sign: &'static str,
24    negative_sign: &'static str,
25    int_frac_digits: u8,
26    frac_digits: u8,
27    p_cs_precedes: bool,
28    p_sep_by_space: bool,
29    p_sign_posn: u8,
30    n_cs_precedes: bool,
31    n_sep_by_space: bool,
32    n_sign_posn: u8,
33}
34
35const DEFAULT_MONETARY: LocaleMonetaryInfo = LocaleMonetaryInfo {
36    int_curr_symbol: "USD ",
37    currency_symbol: "$",
38    mon_decimal_point: ".",
39    mon_thousands_sep: ",",
40    mon_grouping: &[3, 3],
41    positive_sign: "",
42    negative_sign: "-",
43    int_frac_digits: 2,
44    frac_digits: 2,
45    p_cs_precedes: true,
46    p_sep_by_space: false,
47    p_sign_posn: 1,
48    n_cs_precedes: true,
49    n_sep_by_space: false,
50    n_sign_posn: 1,
51};
52
53#[derive(Default)]
54struct FormatFlags {
55    left_justify: bool,
56    force_sign: bool,
57    space_sign: bool,
58    use_parens: bool,
59    suppress_symbol: bool,
60    field_width: Option<usize>,
61    left_precision: Option<usize>,
62    right_precision: Option<usize>,
63    international: bool,
64}
65
66/// Formats a monetary value according to the current locale.
67fn apply_grouping(int_str: &str, monetary: &LocaleMonetaryInfo) -> String {
68    let mut grouped = String::with_capacity(int_str.len() * 2);
69    let mut group_idx = 0;
70    let current_grouping = &monetary.mon_grouping;
71    let separator = monetary.mon_thousands_sep;
72
73    for (count, c) in int_str.chars().enumerate() {
74        if count > 0 {
75            let current_group = current_grouping[group_idx.min(current_grouping.len() - 1)];
76            if current_group > 0 && (count as u8).is_multiple_of(current_group) {
77                grouped.push_str(separator);
78                if group_idx + 1 < current_grouping.len() {
79                    group_idx += 1;
80                }
81            }
82        }
83        grouped.push(c);
84    }
85
86    grouped
87}
88
89/// Safe handling of large monetary values. Returns None if the value is too large to format
90fn format_value_parts(value: f64, frac_digits: usize) -> Option<(String, i64)> {
91    let abs_value = fabs(value);
92    if abs_value > (i64::MAX as f64) {
93        // Check if the value is too large to format
94        return None;
95    }
96
97    let int_part = trunc(abs_value) as i64;
98    let scale = pow(10.0, frac_digits as f64);
99    let frac_part = round((abs_value - int_part as f64) * scale) as i64;
100
101    Some((int_part.to_string(), frac_part))
102}