178 lines
5.3 KiB
Rust
178 lines
5.3 KiB
Rust
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<Utc>,
|
|
}
|
|
|
|
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<u32>,
|
|
base_currency: Option<String>,
|
|
target_currency: Option<String>,
|
|
rate: Option<f64>,
|
|
timestamp: Option<DateTime<Utc>>,
|
|
}
|
|
|
|
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<S: Into<String>>(mut self, base_currency: S) -> Self {
|
|
self.base_currency = Some(base_currency.into());
|
|
self
|
|
}
|
|
|
|
/// Set the target currency
|
|
pub fn target_currency<S: Into<String>>(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<Utc>) -> Self {
|
|
self.timestamp = Some(timestamp);
|
|
self
|
|
}
|
|
|
|
/// Build the ExchangeRate object
|
|
pub fn build(self) -> Result<ExchangeRate, &'static str> {
|
|
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<Mutex<HashMap<String, ExchangeRate>>>,
|
|
}
|
|
|
|
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<ExchangeRate> {
|
|
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<f64> {
|
|
// 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
|
|
};
|
|
} |