smartcalc_tauri/formatter/
mod.rs

1/*
2 * smartcalc v1.0.8
3 * Copyright (c) Erhan BARIS (Ruslan Ognyanov Asenov)
4 * Licensed under the GNU General Public License v2.0.
5 */
6
7use alloc::{string::String};
8use alloc::format;
9use alloc::string::ToString;
10use crate::session::Session;
11use crate::tools::do_divition;
12use core::ops::Deref;
13
14use crate::config::SmartCalcConfig;
15use crate::types::{SmartCalcAstType};
16use crate::constants::MonthInfo;
17
18pub const MINUTE: i64 = 60;
19pub const HOUR: i64 = MINUTE * 60;
20pub const DAY: i64 = HOUR * 24;
21pub const WEEK: i64 = DAY * 7;
22pub const MONTH: i64 = DAY * 30;
23pub const YEAR: i64 = DAY * 365;
24
25fn fract_information(f: f64) -> u64 {
26    let eps = 1e-4;
27    let mut f = f.abs().fract();
28    if f == 0.0 { return 0; }
29    
30    while (f.round() - f).abs() <= eps {
31        f *= 10.0;
32    }
33    
34    while (f.round() - f).abs() > eps {
35        f *= 10.0;
36    }
37    
38    f.round() as u64
39}
40
41pub fn left_padding(number: i64, size: usize) -> String {
42    format!("{:0width$}", &number, width = size)
43}
44
45pub fn format_number(number: f64, thousands_separator: String, decimal_separator: String, decimal_digits: u8, remove_fract_if_zero: bool, use_fract_rounding: bool) -> String {
46    let divider      = 10_u32.pow(decimal_digits.into());
47    let fract_number = do_divition((number * divider as f64).round(), divider as f64);
48    let trunc_part   = fract_number.trunc().abs().to_string();
49
50    let formated_number = match use_fract_rounding {
51        true => format!("{:.width$}", &number.abs(), width = decimal_digits.into()),
52        false => format!("{}", &number.abs())
53    };
54
55    let fract_part = fract_information(fract_number.fract());
56    let trunc_size = trunc_part.len();
57    let mut trunc_dot_index = 3 - (trunc_part.len() % 3);
58    let mut trunc_formated = String::new();
59
60
61    if number < 0.0 {
62        trunc_formated.push('-');
63    }
64
65    for index in 0..trunc_size {
66        trunc_formated.push(formated_number.chars().nth(index).unwrap());
67        trunc_dot_index += 1;
68        if trunc_size != (index + 1) && trunc_dot_index % 3 == 0 {
69            trunc_formated.push_str(&thousands_separator);
70        }
71    }
72    
73    if (fract_part > 0 || !remove_fract_if_zero) && trunc_size != formated_number.len() {
74        trunc_formated.push_str(&decimal_separator);
75
76        for index in (trunc_size+1)..formated_number.len() {
77            trunc_formated.push(formated_number.chars().nth(index).unwrap());
78        }
79    }
80
81    trunc_formated
82}
83
84pub fn get_month_info(config: &SmartCalcConfig, language: &'_ str, month: u8) -> Option<MonthInfo> {
85    match config.month_regex.get(language) {
86        Some(month_list) => month_list.get((month - 1) as usize).map(|(_, month)| month.clone()),
87        None => None
88    }
89}
90
91pub fn uppercase_first_letter(s: &'_ str) -> String {
92    let mut c = s.chars();
93    match c.next() {
94        None => String::new(),
95        Some(f) => f.to_uppercase().collect::<String>() + c.as_str(),
96    }
97}
98
99pub fn format_result(config: &SmartCalcConfig, session: &Session, result: alloc::rc::Rc<SmartCalcAstType>) -> String {
100    match result.deref() {
101        SmartCalcAstType::Item(item) => item.print(config, session),
102        _ => "".to_string()
103    }
104}
105
106#[cfg(test)]
107#[test]
108fn get_frac_test() {
109    assert_eq!(fract_information(0.1234567), 1234567);
110    assert_eq!(fract_information(987654321.987), 987);
111}
112
113#[cfg(test)]
114#[test]
115fn format_number_test() {
116    assert_eq!(format_number(123.0, ",".to_string(), ".".to_string(), 2, false, true), "123.00".to_string());
117    assert_eq!(format_number(123.1, ",".to_string(), ".".to_string(), 2, false, true), "123.10".to_string());
118    assert_eq!(format_number(123.01, ",".to_string(), ".".to_string(), 2, false, true), "123.01".to_string());
119    assert_eq!(format_number(1234.01, ",".to_string(), ".".to_string(), 2, false, true), "1,234.01".to_string());
120    assert_eq!(format_number(123456.01, ",".to_string(), ".".to_string(), 2, false, true), "123,456.01".to_string());
121    assert_eq!(format_number(123456.123456789, ",".to_string(), ".".to_string(), 2, false, true), "123,456.12".to_string());
122    assert_eq!(format_number(123456.1, ",".to_string(), ".".to_string(), 2, false, true), "123,456.10".to_string());
123    assert_eq!(format_number(-123456.1, ",".to_string(), ".".to_string(), 2, false, true), "-123,456.10".to_string());
124
125    assert_eq!(format_number(123.0, ",".to_string(), ".".to_string(), 2, true, false), "123".to_string());
126    assert_eq!(format_number(123.0000, ",".to_string(), ".".to_string(), 2, true, false), "123".to_string());
127    assert_eq!(format_number(123.1, ",".to_string(), ".".to_string(), 2, false, false), "123.1".to_string());
128    assert_eq!(format_number(123.01, ",".to_string(), ".".to_string(), 2, false, false), "123.01".to_string());
129    assert_eq!(format_number(1234.01, ",".to_string(), ".".to_string(), 2, false, false), "1,234.01".to_string());
130    assert_eq!(format_number(123456.01, ",".to_string(), ".".to_string(), 2, false, false), "123,456.01".to_string());
131    assert_eq!(format_number(123456.123456789, ",".to_string(), ".".to_string(), 2, false, false), "123,456.123456789".to_string());
132    assert_eq!(format_number(123456.1, ",".to_string(), ".".to_string(), 2, false, false), "123,456.1".to_string());
133    assert_eq!(format_number(-123456.1, ",".to_string(), ".".to_string(), 2, false, false), "-123,456.1".to_string());
134}
135
136#[cfg(test)]
137#[test]
138fn format_result_test() {
139    use alloc::rc::Rc;
140    use crate::compiler::DataItem;
141    use crate::compiler::number::NumberItem;
142    use crate::compiler::time::TimeItem;
143    use crate::config::SmartCalcConfig;
144    use crate::types::NumberType;
145    let config = SmartCalcConfig::default();
146
147    let mut session = Session::default();
148    session.set_language("en".to_string());
149    assert_eq!(NumberItem(123456.123456789, NumberType::Decimal).print(&config, &session), "123.456,12".to_string());
150    assert_eq!(NumberItem(1.123456789, NumberType::Decimal).print(&config, &session), "1,12".to_string());
151    assert_eq!(NumberItem(2.0, NumberType::Hexadecimal).print(&config, &session), "0x2".to_string());
152            
153    assert_eq!(format_result(&config, &session, Rc::new(SmartCalcAstType::Item(Rc::new(TimeItem(chrono::Utc::today().and_hms(11, 30, 0).naive_utc(), config.get_time_offset()))))), "11:30:00 UTC".to_string());
154    assert_eq!(format_result(&config, &session, Rc::new(SmartCalcAstType::Item(Rc::new(TimeItem(chrono::Utc::today().and_hms(0, 0, 0).naive_utc(), config.get_time_offset()))))), "00:00:00 UTC".to_string());
155}