mod from_str;
mod try_from;
use core::ops::Range;
use std::{collections::HashMap, env, fs, path::PathBuf};
use chrono::{Datelike, Local};
use crate::{request, Currency, Decimal, Result};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ExchangeRates(pub(crate) HashMap<Currency, Decimal>);
impl ExchangeRates
{
fn filepath() -> PathBuf
{
let today = Local::now();
env::temp_dir().join(format!(
"money2--{}-{}-{}.csv",
today.year(),
today.month(),
today.day()
))
}
pub fn get(&self, current: &Currency, desired: &Currency) -> Option<Decimal>
{
self.0.get(current).and_then(|c| self.0.get(desired).map(|d| d / c))
}
pub fn index(&self, range: Range<&Currency>) -> Decimal
{
self.get(range.start, range.end).unwrap_or_else(|| {
panic!("Either {} or {} was not found in {self:?}", range.start, range.end)
})
}
pub async fn new() -> Result<Self>
{
match Self::filepath()
{
path if path.exists() => fs::read_to_string(path)?,
path =>
{
let csv_contents = request::get_unzipped(
"https://www.ecb.europa.eu/stats/eurofxref/eurofxref.zip",
)
.await?;
fs::write(path, &csv_contents)?;
csv_contents
},
}
.parse()
}
}
#[cfg(test)]
mod tests
{
use std::fs;
use super::ExchangeRates;
#[tokio::test]
async fn new()
{
let filepath = ExchangeRates::filepath();
if filepath.exists()
{
fs::remove_file(&filepath).unwrap();
}
assert!(!filepath.is_file());
let downloaded = ExchangeRates::new().await.unwrap();
assert!(filepath.is_file());
let cached = ExchangeRates::new().await.unwrap();
assert!(filepath.is_file());
assert_eq!(downloaded, cached);
}
}