db/herodb_old/src/models/biz/exchange_rate.rs
2025-04-22 12:53:17 +04:00

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
};
}