mirror of
				https://github.com/lov3b/ecb-rates.git
				synced 2025-10-30 21:00:26 +01:00 
			
		
		
		
	Core is done
This commit is contained in:
		
							
								
								
									
										7
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										7
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -66,6 +66,12 @@ dependencies = [ | |||||||
|  "windows-sys 0.59.0", |  "windows-sys 0.59.0", | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "anyhow" | ||||||
|  | version = "1.0.95" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "atomic-waker" | name = "atomic-waker" | ||||||
| version = "1.1.2" | version = "1.1.2" | ||||||
| @@ -209,6 +215,7 @@ dependencies = [ | |||||||
| name = "ecb-exchange" | name = "ecb-exchange" | ||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  |  "anyhow", | ||||||
|  "clap", |  "clap", | ||||||
|  "quick-xml", |  "quick-xml", | ||||||
|  "reqwest", |  "reqwest", | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ version = "0.1.0" | |||||||
| edition = "2021" | edition = "2021" | ||||||
|  |  | ||||||
| [dependencies] | [dependencies] | ||||||
|  | anyhow = "1.0.95" | ||||||
| clap = { version = "4.5.23", features = ["derive"] } | clap = { version = "4.5.23", features = ["derive"] } | ||||||
| quick-xml = { version = "0.37.2", features = ["async-tokio", "tokio"] } | quick-xml = { version = "0.37.2", features = ["async-tokio", "tokio"] } | ||||||
| reqwest = "0.12.12" | reqwest = "0.12.12" | ||||||
|   | |||||||
							
								
								
									
										61
									
								
								src/cli.rs
									
									
									
									
									
								
							
							
						
						
									
										61
									
								
								src/cli.rs
									
									
									
									
									
								
							| @@ -1,4 +1,6 @@ | |||||||
| use clap::{arg, Parser, Subcommand}; | use clap::{arg, Parser, ValueEnum}; | ||||||
|  |  | ||||||
|  | use crate::ecb_url; | ||||||
|  |  | ||||||
| #[derive(Debug, Parser)] | #[derive(Debug, Parser)] | ||||||
| #[command(author, version, about)] | #[command(author, version, about)] | ||||||
| @@ -7,25 +9,44 @@ pub struct Cli { | |||||||
|     /// Which currencies do you want to fetch rates for? |     /// Which currencies do you want to fetch rates for? | ||||||
|     #[arg(long = "currencies", short = 'c')] |     #[arg(long = "currencies", short = 'c')] | ||||||
|     pub currencies: Vec<String>, |     pub currencies: Vec<String>, | ||||||
|     /// Which subcommand (output format) we are using |  | ||||||
|     #[command(subcommand)] |     #[arg(value_enum, default_value_t = FormatOption::Plain)] | ||||||
|     pub command: FormatCommand, |     pub command: FormatOption, | ||||||
|  |  | ||||||
|  |     /// Show the time in the output | ||||||
|  |     #[arg(long = "display-time")] | ||||||
|  |     pub display_time: bool, | ||||||
|  |  | ||||||
|  |     /// Print currencies in a compact single line | ||||||
|  |     #[arg(long = "compact")] | ||||||
|  |     pub compact: bool, | ||||||
|  |  | ||||||
|  |     /// Amount of data | ||||||
|  |     #[arg(value_enum, default_value_t = Resolution::TODAY, long="resolution", short='r')] | ||||||
|  |     pub resolution: Resolution, | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Subcommand enum for output format | #[derive(Debug, Clone, Copy, ValueEnum)] | ||||||
| #[derive(Debug, Subcommand)] | pub enum Resolution { | ||||||
| pub enum FormatCommand { |     TODAY, | ||||||
|     /// Minimal JSON output |     HistDays90, | ||||||
|     JSONMin, |     HistDay, | ||||||
|     /// Pretty-printed JSON output | } | ||||||
|     JSONPretty, |  | ||||||
|     /// Plain line-by-line output (with extra flags) | impl Resolution { | ||||||
|     Plain { |     pub fn to_ecb_url(&self) -> &'static str { | ||||||
|         /// Show the time in the output |         match self { | ||||||
|         #[arg(long = "display-time")] |             Resolution::TODAY => ecb_url::TODAY, | ||||||
|         display_time: bool, |             Resolution::HistDays90 => ecb_url::hist::DAYS_90, | ||||||
|         /// Print currencies in a compact single line |             Resolution::HistDay => ecb_url::hist::DAILY, | ||||||
|         #[arg(long = "compact")] |         } | ||||||
|         compact: bool, |     } | ||||||
|     }, | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, Clone, Copy, ValueEnum)] | ||||||
|  | pub enum FormatOption { | ||||||
|  |     /// JSON output | ||||||
|  |     Json, | ||||||
|  |     /// Plain line-by-line output (with extra flags) | ||||||
|  |     Plain, | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										110
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										110
									
								
								src/main.rs
									
									
									
									
									
								
							| @@ -1,14 +1,106 @@ | |||||||
| use ecb_exchange::ecb_url; | use clap::Parser as _; | ||||||
| use reqwest::Client; | use ecb_exchange::{cli::Cli, models::ExchangeRateResult}; | ||||||
| use std::error::Error; | use reqwest::{Client, IntoUrl}; | ||||||
|  | use std::{borrow::BorrowMut, collections::HashMap, error::Error, process::ExitCode}; | ||||||
|  |  | ||||||
| use ecb_exchange::parsing::parse; | use ecb_exchange::parsing::parse; | ||||||
|  |  | ||||||
| #[tokio::main(flavor = "current_thread")] | async fn get_and_parse(url: impl IntoUrl) -> Result<Vec<ExchangeRateResult>, Box<dyn Error>> { | ||||||
| async fn main() -> Result<(), Box<dyn Error>> { |  | ||||||
|     let client = Client::new(); |     let client = Client::new(); | ||||||
|     let xml_content = client.get(ecb_url::TODAY).send().await?.text().await?; |     let xml_content = client.get(url).send().await?.text().await?; | ||||||
|     let parsed = parse(&xml_content).unwrap(); |     parse(&xml_content) | ||||||
|     println!("{}", serde_json::to_string_pretty(&parsed).unwrap()); | } | ||||||
|     Ok(()) |  | ||||||
|  | fn filter_currencies(exchange_rate_results: &mut [ExchangeRateResult], currencies: &[String]) { | ||||||
|  |     for exchange_rate in exchange_rate_results { | ||||||
|  |         let rates_ptr: *mut HashMap<String, f64> = &mut exchange_rate.rates; | ||||||
|  |         exchange_rate | ||||||
|  |             .rates | ||||||
|  |             .keys() | ||||||
|  |             .filter(|x| !currencies.contains(x)) | ||||||
|  |             .for_each(|key_to_remove| { | ||||||
|  |                 let rates = unsafe { (*rates_ptr).borrow_mut() }; | ||||||
|  |                 rates.remove_entry(key_to_remove); | ||||||
|  |             }); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[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; | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     if !cli.currencies.is_empty() { | ||||||
|  |         filter_currencies(&mut parsed, &cli.currencies); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     let output = match cli.command { | ||||||
|  |         ecb_exchange::cli::FormatOption::Json => { | ||||||
|  |             let mut json_values = parsed | ||||||
|  |                 .iter() | ||||||
|  |                 .map(|x| serde_json::to_value(x).expect("Failed to parse content as JSON value")) | ||||||
|  |                 .collect::<Vec<_>>(); | ||||||
|  |  | ||||||
|  |             if !cli.display_time { | ||||||
|  |                 for json_value in json_values.iter_mut() { | ||||||
|  |                     if let Some(map) = json_value.as_object_mut() { | ||||||
|  |                         map.remove_entry("time"); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if cli.compact { | ||||||
|  |                 serde_json::to_string(&json_values) | ||||||
|  |             } else { | ||||||
|  |                 serde_json::to_string_pretty(&json_values) | ||||||
|  |             } | ||||||
|  |             .expect("Failed to parse content as JSON") | ||||||
|  |         } | ||||||
|  |         ecb_exchange::cli::FormatOption::Plain => { | ||||||
|  |             struct StringCur<'a> { | ||||||
|  |                 time: &'a String, | ||||||
|  |                 cur: String, | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             let separator = if cli.compact { ", " } else { "\n" }; | ||||||
|  |  | ||||||
|  |             let string_curred = parsed.iter().map(|entry| { | ||||||
|  |                 let s = entry | ||||||
|  |                     .rates | ||||||
|  |                     .iter() | ||||||
|  |                     .map(|(cur, rate)| format!("{}: {}", cur, rate)) | ||||||
|  |                     .collect::<Vec<_>>() | ||||||
|  |                     .join(&separator); | ||||||
|  |  | ||||||
|  |                 StringCur { | ||||||
|  |                     time: &entry.time, | ||||||
|  |                     cur: s, | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             let time_sep = if cli.compact { ": " } else { "\n" }; | ||||||
|  |             let mut buf = String::new(); | ||||||
|  |             for sc in string_curred { | ||||||
|  |                 if cli.display_time { | ||||||
|  |                     buf.push_str(&sc.time); | ||||||
|  |                     buf.push_str(time_sep); | ||||||
|  |                 } | ||||||
|  |                 buf.push_str(&sc.cur); | ||||||
|  |                 buf.push_str(&separator); | ||||||
|  |                 buf.push('\n'); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             buf | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     println!("{}", &output); | ||||||
|  |     ExitCode::SUCCESS | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user