use crate::types::CalcResult; use chrono::NaiveDate; use std::collections::HashMap; /// Evaluation context holding exchange rates, current date, variables, and other state /// needed during interpretation. pub struct EvalContext { /// Exchange rates relative to USD. Key is currency code (e.g. "EUR"), /// value is how many units of that currency per 1 USD. pub exchange_rates: HashMap, /// The current date for resolving `today`. pub today: NaiveDate, /// Named variables set via assignment expressions (e.g., `x = 5`). pub variables: HashMap, } impl EvalContext { pub fn new() -> Self { Self { exchange_rates: HashMap::new(), today: chrono::Local::now().date_naive(), variables: HashMap::new(), } } /// Create a context with a fixed date (useful for testing). pub fn with_date(today: NaiveDate) -> Self { Self { exchange_rates: HashMap::new(), today, variables: HashMap::new(), } } /// Set exchange rate: 1 USD = `rate` units of `currency`. pub fn set_rate(&mut self, currency: &str, rate: f64) { self.exchange_rates.insert(currency.to_string(), rate); } /// Store a variable result. pub fn set_variable(&mut self, name: &str, result: CalcResult) { self.variables.insert(name.to_string(), result); } /// Retrieve a variable result. pub fn get_variable(&self, name: &str) -> Option<&CalcResult> { self.variables.get(name) } /// Convert `amount` from `from_currency` to `to_currency`. /// Returns None if either rate is missing. pub fn convert_currency( &self, amount: f64, from_currency: &str, to_currency: &str, ) -> Option { let from_rate = if from_currency == "USD" { 1.0 } else { *self.exchange_rates.get(from_currency)? }; let to_rate = if to_currency == "USD" { 1.0 } else { *self.exchange_rates.get(to_currency)? }; let usd_amount = amount / from_rate; Some(usd_amount * to_rate) } } impl Default for EvalContext { fn default() -> Self { Self::new() } }