diff --git a/src/cache/cache.rs b/src/cache/cache.rs index f4e2876..c28bfba 100644 --- a/src/cache/cache.rs +++ b/src/cache/cache.rs @@ -3,8 +3,8 @@ use std::io::{BufReader, BufWriter}; use std::path::{Path, PathBuf}; use super::CacheLine; -use crate::cli::View; use crate::os::Os; +use crate::View; #[derive(Debug)] pub struct Cache { @@ -13,7 +13,7 @@ pub struct Cache { } impl Cache { - pub fn load(view: View) -> Option { + pub fn load(view: &View) -> Option { let config_opt = Os::get_current()?.get_config_path(); let mut config_path = match config_opt { Ok(k) => k, diff --git a/src/cli.rs b/src/cli/cli_t.rs similarity index 54% rename from src/cli.rs rename to src/cli/cli_t.rs index 9c73d30..9b5d04e 100644 --- a/src/cli.rs +++ b/src/cli/cli_t.rs @@ -1,6 +1,6 @@ use clap::{arg, Parser, ValueEnum}; -use crate::ecb_url; +use super::{ShowDays, SortBy}; #[derive(Debug, Parser)] #[command(author, version, about)] @@ -30,7 +30,7 @@ pub struct Cli { pub force_color: bool, /// Sort by the currency name (in alphabetical order), or by the rate value (low -> high) - #[arg(value_enum, long = "sort-by", short = 's', default_value_t = SortBy::Currency)] + #[arg(value_enum, long = "sort-by", default_value_t = SortBy::Currency)] pub sort_by: SortBy, /// Recalculate to the perspective from an included currency @@ -46,51 +46,8 @@ pub struct Cli { pub max_decimals: u8, /// Amount of data - #[arg(value_enum, default_value_t = View::TODAY, long="view", short='v')] - pub view: View, -} - -#[derive(Debug, Clone, Copy, ValueEnum)] -pub enum SortBy { - Currency, - Rate, -} - -#[derive(Debug, Clone, Copy, ValueEnum)] -pub enum View { - #[clap(name = "last-day")] - TODAY, - #[clap(name = "last-90-days")] - HistDays90, - #[clap(name = "all-days")] - HistDaysAll, -} - -impl View { - pub fn to_ecb_url(&self) -> &'static str { - match self { - View::TODAY => ecb_url::TODAY, - View::HistDays90 => ecb_url::hist::DAYS_90, - View::HistDaysAll => ecb_url::hist::DAYS_ALL, - } - } - - pub fn get_name(&self) -> &'static str { - match self { - View::TODAY => "today", - View::HistDays90 => "last-90-days", - View::HistDaysAll => "all-days", - } - } -} - -impl SortBy { - pub fn get_comparer(&self) -> fn(&(&str, f64), &(&str, f64)) -> std::cmp::Ordering { - match self { - Self::Currency => |a, b| a.0.cmp(&b.0), - Self::Rate => |a, b| a.1.total_cmp(&b.1), - } - } + #[arg(default_value_t = ShowDays::Days(1), long="show_days", short='s')] + pub show_days: ShowDays, } #[derive(Debug, Clone, Copy, ValueEnum)] diff --git a/src/cli/mod.rs b/src/cli/mod.rs new file mode 100644 index 0000000..54dee94 --- /dev/null +++ b/src/cli/mod.rs @@ -0,0 +1,7 @@ +mod cli_t; +mod since; +mod sort_by; + +pub use cli_t::{Cli, FormatOption}; +pub use since::ShowDays; +pub use sort_by::SortBy; diff --git a/src/cli/since.rs b/src/cli/since.rs new file mode 100644 index 0000000..ac6ab68 --- /dev/null +++ b/src/cli/since.rs @@ -0,0 +1,54 @@ +use crate::View; +use std::{fmt, str::FromStr}; + +#[derive(Debug, Clone, Copy)] +pub enum ShowDays { + Days(usize), + All, +} + +impl FromStr for ShowDays { + type Err = String; + + fn from_str(s: &str) -> Result { + if s.eq_ignore_ascii_case("all") { + return Ok(ShowDays::All); + } + s.parse::().map(ShowDays::Days).map_err(|_| { + format!( + "Invalid value for since: '{}'. Use a positive integer or 'start'.", + s + ) + }) + } +} +impl fmt::Display for ShowDays { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ShowDays::Days(days) => write!(f, "{}", days), + ShowDays::All => write!(f, "all"), + } + } +} + +impl ShowDays { + /// None represents infinity + pub fn to_option(&self) -> Option { + match self { + ShowDays::Days(d) => Some(*d), + ShowDays::All => None, + } + } + + pub fn to_view(&self) -> Option { + match self { + ShowDays::Days(d) => match d { + 0 => None, + 1 => Some(View::TODAY), + 2..=90 => Some(View::HistDays90), + 91.. => Some(View::HistDaysAll), + }, + ShowDays::All => Some(View::HistDaysAll), + } + } +} diff --git a/src/cli/sort_by.rs b/src/cli/sort_by.rs new file mode 100644 index 0000000..3086333 --- /dev/null +++ b/src/cli/sort_by.rs @@ -0,0 +1,16 @@ +use clap::ValueEnum; + +#[derive(Debug, ValueEnum, Clone)] +pub enum SortBy { + Currency, + Rate, +} + +impl SortBy { + pub fn get_comparer(&self) -> fn(&(&str, f64), &(&str, f64)) -> std::cmp::Ordering { + match self { + Self::Currency => |a, b| a.0.cmp(&b.0), + Self::Rate => |a, b| a.1.total_cmp(&b.1), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 3476748..9c4abd3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,9 +7,11 @@ pub mod os; pub mod parsing; pub mod table; pub mod utils_calc; +mod view; pub use header_description::HeaderDescription; pub use holiday::Hollidays; +pub use view::View; const APP_NAME: &'static str = "ECB-rates"; const DEFAULT_WIDTH: usize = 20; diff --git a/src/main.rs b/src/main.rs index 353e666..573c9af 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,11 +25,14 @@ async fn main() -> ExitCode { let mut header_description = HeaderDescription::new(); let use_cache = !cli.no_cache; - let mut cache = if use_cache { - Cache::load(cli.view) - } else { - None + let view = match cli.show_days.to_view() { + Some(v) => v, + None => { + eprintln!("It doesn't make any sence to fetch 0 days right?"); + return ExitCode::SUCCESS; + } }; + let mut cache = if use_cache { Cache::load(&view) } else { None }; let cache_ok = cache.as_ref().map_or_else( || false, |c| c.get_cache_line().map_or_else(|| false, |cl| cl.is_valid()), @@ -44,7 +47,7 @@ async fn main() -> ExitCode { .exchange_rate_results .clone() } else { - let parsed = match get_and_parse(cli.view.to_ecb_url()).await { + let parsed = match get_and_parse(view.to_ecb_url()).await { Ok(k) => k, Err(e) => { eprintln!("Failed to get/parse data from ECB: {}", e); @@ -102,6 +105,18 @@ async fn main() -> ExitCode { filter_currencies(&mut parsed, ¤cies); } + parsed.reverse(); + let parsed = match cli.show_days.to_option() { + Some(n) => { + if parsed.len() <= n { + parsed.as_slice() + } else { + &parsed[0..n] + } + } + None => parsed.as_slice(), + }; + let output = match cli.command { FormatOption::Json => { let mut json_values = parsed diff --git a/src/view.rs b/src/view.rs new file mode 100644 index 0000000..6dbb4f4 --- /dev/null +++ b/src/view.rs @@ -0,0 +1,25 @@ +use crate::ecb_url; + +pub enum View { + TODAY, + HistDays90, + HistDaysAll, +} + +impl View { + pub fn to_ecb_url(&self) -> &'static str { + match self { + Self::TODAY => ecb_url::TODAY, + Self::HistDays90 => ecb_url::hist::DAYS_90, + Self::HistDaysAll => ecb_url::hist::DAYS_ALL, + } + } + + pub fn get_name(&self) -> &'static str { + match self { + Self::TODAY => "today", + Self::HistDays90 => "last-90-days", + Self::HistDaysAll => "all-days", + } + } +}