locale_settings/
numeric.rs

1/*!
2Fetch locale-specific number formatting settings.
3
4This module provides basic formatting rules for most rational
5numbers; floating point numbers in scientific notation, fractional
6numbers, and imaginary numbers are not covered.
7
8## Example
9
10```
11use locale_settings::locale::{Category, get_locale};
12use locale_settings::numeric::get_numeric_format;
13use locale_types::Locale;
14
15if get_locale(&Category::Currency).unwrap() == Locale::POSIX {
16    let format = get_numeric_format();
17    assert_eq!(format.decimal_separator, ".");
18} else {
19    panic!("expecting POSIX locale");
20}
21```
22*/
23
24use crate::ffi::localeconv;
25use crate::ffi::utils::*;
26use crate::Category;
27use locale_types::{Locale, LocaleResult};
28
29// ------------------------------------------------------------------------------------------------
30// Public Types
31// ------------------------------------------------------------------------------------------------
32
33/// Numeric formatting settings.
34#[derive(Debug, Clone)]
35pub struct NumericFormat {
36    /// The symbol to use for positive values.
37    pub positive_sign: String,
38    /// The symbol to use for negative values.
39    pub negative_sign: String,
40    /// The radix character.
41    pub decimal_separator: String, // TODO: is radix_character better?
42    /// The separator between groups.
43    pub thousands_separator: String, // TODO: is group_separator better?
44    /// The grouping rules.
45    pub grouping: Vec<usize>,
46}
47
48// ------------------------------------------------------------------------------------------------
49// Public Functions
50// ------------------------------------------------------------------------------------------------
51
52/// Fetch the numeric formatting settings for the current locale.
53pub fn get_numeric_format() -> NumericFormat {
54    unsafe {
55        let lconv = localeconv();
56        NumericFormat {
57            positive_sign: cstr_to_string((*lconv).positive_sign),
58            negative_sign: cstr_to_string((*lconv).negative_sign),
59            decimal_separator: cstr_to_string((*lconv).decimal_point),
60            thousands_separator: cstr_to_string((*lconv).thousands_sep),
61            grouping: grouping_vector((*lconv).grouping),
62        }
63    }
64}
65
66/// Fetch the numeric formatting rules for a specified `Locale`.
67///
68/// # Arguments
69///
70/// * `locale` - The locale to query.
71/// * `inherit_current` - Whether the specified locale should inherit
72///   from the current locale.
73///
74/// If `inherit_current` is `false` the `locale` specified will be treated
75/// as an entirely new and complete locale when calling the C
76/// [`newlocale`](https://man.openbsd.org/newlocale.3) function. If it is
77/// `true` the `locale` is assumed to be a partially specified one and inherits
78/// any unspecified components from the current locale. For example, if the
79/// current locale is `en_US.UTF-8` and the parameters passed are `_NZ` and
80/// `true` then the resulting locale will be `en_NZ.UTF-8`.
81pub fn get_numeric_format_for_locale(
82    locale: Locale,
83    inherit_current: bool,
84) -> LocaleResult<NumericFormat> {
85    get_format_for_locale(
86        Category::Numeric,
87        locale,
88        &get_numeric_format,
89        inherit_current,
90    )
91}
92
93#[cfg(experimental)]
94pub mod fmt {
95    use std::fmt::Display;
96
97    pub fn format<T>(value: T) -> String
98    where
99        T: Display,
100    {
101        let initial = value.to_string();
102        if initial.chars().all(|c| c.is_digit(10) || c == '.') {
103            initial
104        } else {
105            panic!("doesn't look like a nunber");
106        }
107    }
108}
109
110// ------------------------------------------------------------------------------------------------
111// Unit Tests
112// ------------------------------------------------------------------------------------------------
113
114#[cfg(test)]
115mod tests {
116    use crate::locale::{set_locale, Category};
117    use crate::numeric::get_numeric_format;
118    use locale_types::Locale;
119
120    // --------------------------------------------------------------------------------------------
121    #[test]
122    fn test_numeric_settings() {
123        if set_locale(&Locale::POSIX, &Category::Numeric) {
124            let format = get_numeric_format();
125            println!("{:#?}", format);
126            assert_eq!(format.decimal_separator, ".");
127        } else {
128            panic!("set_locale returned false");
129        }
130    }
131}