smartcalc_tauri/compiler/
money.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 core::any::{Any, TypeId};
8use alloc::format;
9use alloc::rc::Rc;
10use alloc::string::ToString;
11use alloc::string::String;
12use core::ops::Deref;
13use crate::session::Session;
14use crate::config::SmartCalcConfig;
15use crate::types::{CurrencyInfo, TokenType, NumberType};
16
17use super::number::NumberItem;
18use super::{DataItem, OperationType, UnaryType};
19use crate::formatter::format_number;
20use crate::tools::do_divition;
21
22#[derive(Debug)]
23
24pub struct MoneyItem(pub f64, pub Rc<CurrencyInfo>);
25
26impl MoneyItem {
27    pub fn get_currency(&self) -> Rc<CurrencyInfo> {
28        self.1.clone()
29    }
30    
31    pub fn get_price(&self) -> f64 {
32        self.0
33    }
34    
35    fn convert_currency(&self, config: &SmartCalcConfig, left: &MoneyItem) -> f64 {
36        let as_usd = match config.currency_rate.get(&left.get_currency()) {
37            Some(l_rate) => do_divition(left.get_price(), *l_rate),
38            _ => 0.0
39        };
40    
41        match config.currency_rate.get(&self.get_currency()) {
42            Some(r_rate) => as_usd * r_rate,
43            _ => 0.0
44        }
45    }
46}
47
48impl DataItem for MoneyItem {
49    fn as_token_type(&self) -> TokenType {
50        TokenType::Money(self.0, self.1.clone())
51    }
52    fn is_same(&self, other: &dyn Any) -> bool {
53        match other.downcast_ref::<(f64, Rc<CurrencyInfo>)>() {
54            Some((l_value, l_symbol)) => (l_value - self.0).abs() < f64::EPSILON && l_symbol.deref() == self.1.deref(),
55            None => false
56        }
57    }
58    fn as_any(&self) -> &dyn Any { self }
59    
60    fn calculate(&self, config: &SmartCalcConfig, on_left: bool, other: &dyn DataItem, operation_type: OperationType) -> Option<Rc<dyn DataItem>> {
61        /* If both item is money and current money is on left side, skip calculation */
62        let (other_amount, target_curreny, is_other_money)  = match other.type_name() {
63            "NUMBER" => (other.get_underlying_number(), self.1.clone(), false),
64            "MONEY" => (self.convert_currency(config, other.as_any().downcast_ref::<MoneyItem>()?), self.1.clone(), true),
65            "PERCENT" => (other.get_number(self), self.1.clone(), false),
66            "DURATION" => (other.get_number(self), self.1.clone(), false),
67            _ => return None
68        };
69        
70        let (left, right) = if on_left { 
71            (self.0, other_amount) 
72        } else { 
73            (other_amount, self.0 ) 
74        };
75        
76        let result = match operation_type {
77            OperationType::Add => left + right,
78            OperationType::Div => {
79                let div_result = do_divition(left, right);
80                match is_other_money {
81                    true => return Some(Rc::new(NumberItem(div_result, NumberType::Decimal))),
82                    false => div_result
83                }
84            },
85            OperationType::Mul => left * right,
86            OperationType::Sub => left - right
87        };
88        Some(Rc::new(MoneyItem(result, target_curreny)))
89    }
90    
91    fn get_number(&self, other: &dyn DataItem) -> f64 {
92       if self.type_name() == other.type_name() {
93           return self.0 
94       }
95       
96       other.get_underlying_number() * self.0
97    }
98    
99    fn get_underlying_number(&self) -> f64 { self.0 }
100    fn type_name(&self) -> &'static str { "MONEY" }
101    fn type_id(&self) -> TypeId { TypeId::of::<MoneyItem>() }
102    fn print(&self, config: &SmartCalcConfig, _: &Session) -> String {
103        let currency = self.get_currency();
104        let formated_price = format_number(self.get_price(), config.thousand_separator.to_string(), config.decimal_seperator.to_string(), currency.decimal_digits, config.money_config.remove_fract_if_zero, config.money_config.use_fract_rounding);
105        match (currency.symbol_on_left, currency.space_between_amount_and_symbol) {
106            (true, true) => format!("{} {}", currency.symbol, formated_price),
107            (true, false) => format!("{}{}", currency.symbol, formated_price),
108            (false, true) => format!("{} {}", formated_price, currency.symbol),
109            (false, false) => format!("{}{}", formated_price, currency.symbol),
110        }
111    }
112    fn unary(&self, unary: UnaryType) -> Rc<dyn DataItem> {
113        match unary {
114            UnaryType::Minus => Rc::new(Self(-1.0 * self.0, self.1.clone())),
115            UnaryType::Plus => Rc::new(Self(self.0, self.1.clone()))
116        }
117    }
118}
119
120
121#[cfg(test)]
122#[test]
123fn format_result_test_1() {
124    use crate::compiler::money::MoneyItem;
125    use crate::config::SmartCalcConfig;
126    let config = SmartCalcConfig::default();
127    let session = Session::default();
128
129    let usd = config.get_currency("usd".to_string()).unwrap();
130    let tl = config.get_currency("try".to_string()).unwrap();
131    let uzs = config.get_currency("uzs".to_string()).unwrap();
132    let uyu = config.get_currency("uyu".to_string()).unwrap();
133
134    assert_eq!(MoneyItem(0.0, usd.clone()).print(&config, &session), "$0,00".to_string());
135    assert_eq!(MoneyItem(0.05555, usd.clone()).print(&config, &session), "$0,06".to_string());
136    assert_eq!(MoneyItem(123.05555, usd.clone()).print(&config, &session), "$123,06".to_string());
137    assert_eq!(MoneyItem(1234.05555, usd.clone()).print(&config, &session), "$1.234,06".to_string());
138    assert_eq!(MoneyItem(123456.05555, usd.clone()).print(&config, &session), "$123.456,06".to_string());
139    assert_eq!(MoneyItem(123456.0, usd.clone()).print(&config, &session), "$123.456,00".to_string());
140
141    assert_eq!(MoneyItem(0.0, tl.clone()).print(&config, &session), "₺0,00".to_string());
142    assert_eq!(MoneyItem(0.05555, tl.clone()).print(&config, &session), "₺0,06".to_string());
143    assert_eq!(MoneyItem(123.05555, tl.clone()).print(&config, &session), "₺123,06".to_string());
144    assert_eq!(MoneyItem(1234.05555, tl.clone()).print(&config, &session), "₺1.234,06".to_string());
145    assert_eq!(MoneyItem(123456.05555, tl.clone()).print(&config, &session), "₺123.456,06".to_string());
146    assert_eq!(MoneyItem(123456.0, tl.clone()).print(&config, &session), "₺123.456,00".to_string());
147
148    assert_eq!(MoneyItem(0.0, uzs.clone()).print(&config, &session), "0,00 сўм".to_string());
149    assert_eq!(MoneyItem(0.05555, uzs.clone()).print(&config, &session), "0,06 сўм".to_string());
150    assert_eq!(MoneyItem(123.05555, uzs.clone()).print(&config, &session), "123,06 сўм".to_string());
151    assert_eq!(MoneyItem(1234.05555, uzs.clone()).print(&config, &session), "1.234,06 сўм".to_string());
152    assert_eq!(MoneyItem(123456.05555, uzs.clone()).print(&config, &session), "123.456,06 сўм".to_string());
153    assert_eq!(MoneyItem(123456.0, uzs.clone()).print(&config, &session), "123.456,00 сўм".to_string());
154
155    assert_eq!(MoneyItem(0.0, uyu.clone()).print(&config, &session), "$U 0,00".to_string());
156    assert_eq!(MoneyItem(0.05555, uyu.clone()).print(&config, &session), "$U 0,06".to_string());
157    assert_eq!(MoneyItem(123.05555, uyu.clone()).print(&config, &session), "$U 123,06".to_string());
158    assert_eq!(MoneyItem(1234.05555, uyu.clone()).print(&config, &session), "$U 1.234,06".to_string());
159    assert_eq!(MoneyItem(123456.05555, uyu.clone()).print(&config, &session), "$U 123.456,06".to_string());
160    assert_eq!(MoneyItem(123456.0, uyu.clone()).print(&config, &session), "$U 123.456,00".to_string());
161}
162
163
164#[cfg(test)]
165#[test]
166fn format_result_test_2() {
167    use crate::config::SmartCalcConfig;
168    let mut config = SmartCalcConfig::default();
169    let tl = config.get_currency("try".to_string()).unwrap();
170    config.money_config.remove_fract_if_zero = true;
171    config.money_config.use_fract_rounding = true;
172
173    let session = Session::default();
174
175    assert_eq!(MoneyItem(0.0, tl.clone()).print(&config, &session), "₺0".to_string());
176    assert_eq!(MoneyItem(10.0, tl.clone()).print(&config, &session), "₺10".to_string());
177    assert_eq!(MoneyItem(10.1, tl.clone()).print(&config, &session), "₺10,10".to_string());
178}
179
180
181#[cfg(test)]
182#[test]
183fn format_result_test_3() {
184    use crate::config::SmartCalcConfig;
185    let mut config = SmartCalcConfig::default();
186    let tl = config.get_currency("try".to_string()).unwrap();
187    config.money_config.remove_fract_if_zero = false;
188    config.money_config.use_fract_rounding = true;
189
190    let session = Session::default();
191
192    assert_eq!(MoneyItem(0.0, tl.clone()).print(&config, &session), "₺0,00".to_string());
193    assert_eq!(MoneyItem(10.0, tl.clone()).print(&config, &session), "₺10,00".to_string());
194    assert_eq!(MoneyItem(10.1, tl.clone()).print(&config, &session), "₺10,10".to_string());
195}