Cahce read and write

This commit is contained in:
2025-01-07 15:52:01 +01:00
parent 1932ad1187
commit 0bd3c9004f
7 changed files with 191 additions and 9 deletions

92
src/cache.rs Normal file
View File

@ -0,0 +1,92 @@
use std::fs;
use std::io::{BufReader, BufWriter};
use std::path::Path;
use anyhow::Context;
use chrono::serde::ts_seconds;
use chrono::{DateTime, Local, Utc};
use serde::{Deserialize, Serialize};
use crate::models::ExchangeRateResult;
use crate::os::Os;
const FILE_NAME: &'static str = "cache.json";
#[derive(Serialize, Deserialize, Debug)]
pub struct Cache {
#[serde(with = "ts_seconds")]
date: DateTime<Utc>,
#[serde(rename = "camelCase")]
pub exchange_rate_results: Vec<ExchangeRateResult>,
}
impl Cache {
pub fn load() -> Option<Self> {
let config_opt = Os::get_current()?.get_config_path();
let mut config_path = match config_opt {
Ok(k) => k,
Err(e) => {
eprintln!("Failed to locate config dir: {:?}", e);
return None;
}
};
if let Err(e) = fs::create_dir_all(&config_path) {
eprintln!("Failed to create config dir: {:?}", e);
return None;
}
config_path.push(FILE_NAME);
if !config_path.try_exists().unwrap_or_default() {
return None;
}
match Self::read_config(&config_path) {
Ok(k) => Some(k),
Err(e) => {
eprintln!("Config path is invalid, or cannot be created: {:?}", e);
None
}
}
}
pub fn new(exchange_rate_results: Vec<ExchangeRateResult>) -> Self {
let date = Local::now().to_utc();
Self {
exchange_rate_results,
date,
}
}
pub fn save(&self) -> anyhow::Result<()> {
let mut config_path = Os::get_current()
.context("Failed to get config home")?
.get_config_path()?;
fs::create_dir_all(&config_path)?;
eprintln!("Config dir: {}", &config_path.display());
config_path.push(FILE_NAME);
eprintln!("File to write: {}", &config_path.display());
let file = fs::File::options()
.create(true)
.truncate(true)
.open(&config_path)?;
let writer = BufWriter::new(file);
serde_json::to_writer(writer, self)?;
Ok(())
}
pub fn validate(&self) -> bool {
let today = Local::now().naive_local().date();
let saved = self.date.naive_local().date();
saved == today
}
fn read_config(path: &Path) -> anyhow::Result<Self> {
let file = fs::File::open(path)?;
let reader = BufReader::new(file);
Ok(serde_json::from_reader(reader)?)
}
}

View File

@ -21,6 +21,10 @@ pub struct Cli {
#[arg(long = "compact")]
pub compact: bool,
/// Override the cache
#[arg(long = "no-cache")]
pub no_cache: bool,
/// Amount of data
#[arg(value_enum, default_value_t = Resolution::TODAY, long="resolution", short='r')]
pub resolution: Resolution,

View File

@ -3,6 +3,7 @@ pub mod models;
pub mod os;
pub mod parsing;
pub mod table;
pub mod cache;
const APP_NAME: &'static str = "ECB-rates";

View File

@ -1,4 +1,5 @@
use clap::Parser as _;
use ecb_rates::cache::Cache;
use reqwest::{Client, IntoUrl};
use std::{borrow::BorrowMut, collections::HashMap, error::Error, process::ExitCode};
@ -30,12 +31,18 @@ fn filter_currencies(exchange_rate_results: &mut [ExchangeRateResult], currencie
#[tokio::main(flavor = "current_thread")]
async fn main() -> ExitCode {
let cli = Cli::parse();
let mut parsed = match get_and_parse(cli.resolution.to_ecb_url()).await {
Ok(k) => k,
Err(e) => {
eprintln!("Failed to get/parse data from ECB: {}", e);
return ExitCode::FAILURE;
let use_cache = !cli.no_cache;
let cache = if use_cache { Cache::load() } else { None };
let cache_ok = cache.as_ref().map_or_else(|| false, |c| c.validate());
let mut parsed = if cache_ok {
cache.as_ref().unwrap().exchange_rate_results.clone()
} else {
match get_and_parse(cli.resolution.to_ecb_url()).await {
Ok(k) => k,
Err(e) => {
eprintln!("Failed to get/parse data from ECB: {}", e);
return ExitCode::FAILURE;
}
}
};
@ -72,9 +79,9 @@ async fn main() -> ExitCode {
.expect("Failed to parse content as JSON")
}
FormatOption::Plain => parsed
.into_iter()
.iter()
.map(|x| {
let t: Table = x.into();
let t: Table = x.clone().into();
format!("{}", t)
})
.collect::<Vec<_>>()
@ -82,5 +89,10 @@ async fn main() -> ExitCode {
};
println!("{}", &output);
if !cache_ok {
if let Err(e) = Cache::new(parsed).save() {
eprintln!("Failed to save to cache with: {:?}", e);
}
}
ExitCode::SUCCESS
}

View File

@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ExchangeRateResult {
pub time: String,
pub rates: HashMap<String, f64>,