use std::collections::HashMap; use std::sync::{Arc, Mutex}; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use crate::db::model::{Model, Storable}; /// ExchangeRate represents an exchange rate between two currencies #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ExchangeRate { pub id: u32, pub base_currency: String, pub target_currency: String, pub rate: f64, pub timestamp: DateTime, } impl ExchangeRate { /// Create a new exchange rate pub fn new(id: u32, base_currency: String, target_currency: String, rate: f64) -> Self { Self { id, base_currency, target_currency, rate, timestamp: Utc::now(), } } } /// Builder for ExchangeRate pub struct ExchangeRateBuilder { id: Option, base_currency: Option, target_currency: Option, rate: Option, timestamp: Option>, } impl ExchangeRateBuilder { /// Create a new ExchangeRateBuilder with all fields set to None pub fn new() -> Self { Self { id: None, base_currency: None, target_currency: None, rate: None, timestamp: None, } } /// Set the id pub fn id(mut self, id: u32) -> Self { self.id = Some(id); self } /// Set the base currency pub fn base_currency>(mut self, base_currency: S) -> Self { self.base_currency = Some(base_currency.into()); self } /// Set the target currency pub fn target_currency>(mut self, target_currency: S) -> Self { self.target_currency = Some(target_currency.into()); self } /// Set the rate pub fn rate(mut self, rate: f64) -> Self { self.rate = Some(rate); self } /// Set the timestamp pub fn timestamp(mut self, timestamp: DateTime) -> Self { self.timestamp = Some(timestamp); self } /// Build the ExchangeRate object pub fn build(self) -> Result { let now = Utc::now(); Ok(ExchangeRate { id: self.id.ok_or("id is required")?, base_currency: self.base_currency.ok_or("base_currency is required")?, target_currency: self.target_currency.ok_or("target_currency is required")?, rate: self.rate.ok_or("rate is required")?, timestamp: self.timestamp.unwrap_or(now), }) } } impl Storable for ExchangeRate {} // Implement Model trait impl Model for ExchangeRate { fn get_id(&self) -> u32 { self.id } fn db_prefix() -> &'static str { "exchange_rate" } } /// ExchangeRateService provides methods to get and set exchange rates #[derive(Clone)] pub struct ExchangeRateService { rates: Arc>>, } impl ExchangeRateService { /// Create a new exchange rate service pub fn new() -> Self { Self { rates: Arc::new(Mutex::new(HashMap::new())), } } /// Set an exchange rate pub fn set_rate(&self, exchange_rate: ExchangeRate) { let key = format!("{}_{}", exchange_rate.base_currency, exchange_rate.target_currency); let mut rates = self.rates.lock().unwrap(); rates.insert(key, exchange_rate); } /// Get an exchange rate pub fn get_rate(&self, base_currency: &str, target_currency: &str) -> Option { let key = format!("{}_{}", base_currency, target_currency); let rates = self.rates.lock().unwrap(); rates.get(&key).cloned() } /// Convert an amount from one currency to another pub fn convert(&self, amount: f64, from_currency: &str, to_currency: &str) -> Option { // If the currencies are the same, return the amount if from_currency == to_currency { return Some(amount); } // Try to get the direct exchange rate if let Some(rate) = self.get_rate(from_currency, to_currency) { return Some(amount * rate.rate); } // Try to get the inverse exchange rate if let Some(rate) = self.get_rate(to_currency, from_currency) { return Some(amount / rate.rate); } // Try to convert via USD if from_currency != "USD" && to_currency != "USD" { if let Some(from_to_usd) = self.convert(amount, from_currency, "USD") { return self.convert(from_to_usd, "USD", to_currency); } } None } } // Create a global instance of the exchange rate service lazy_static::lazy_static! { pub static ref EXCHANGE_RATE_SERVICE: ExchangeRateService = { let service = ExchangeRateService::new(); // Set some default exchange rates service.set_rate(ExchangeRate::new(1, "USD".to_string(), "EUR".to_string(), 0.85)); service.set_rate(ExchangeRate::new(2, "USD".to_string(), "GBP".to_string(), 0.75)); service.set_rate(ExchangeRate::new(3, "USD".to_string(), "JPY".to_string(), 110.0)); service.set_rate(ExchangeRate::new(4, "USD".to_string(), "CAD".to_string(), 1.25)); service.set_rate(ExchangeRate::new(5, "USD".to_string(), "AUD".to_string(), 1.35)); service }; }