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}