1use 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 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}