Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 34 additions & 26 deletions src/cli_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use colored::*;
use regex::Regex;
use si_unit_prefix::SiUnitPrefix;
use std::error::Error;
use std::fmt;
use std::num::ParseFloatError;
use std::{fmt, process};

use crate::currencies::Currencies;
use crate::defaults::Defaults;
Expand Down Expand Up @@ -66,24 +66,26 @@ impl From<ParseFloatError> for InputError {
}
}

impl From<Args> for CliInput {
fn from(args: Args) -> Self {
Self {
amount: Self::parse_amount(args.amount),
input_currency: Self::parse_input_currency(&args.input_currency),
output_currencies: Self::parse_output_currency(&args.output_currency),
impl TryFrom<Args> for CliInput {
type Error = InputError;

fn try_from(args: Args) -> Result<Self, Self::Error> {
Ok(Self {
amount: Self::parse_amount(args.amount)?,
input_currency: Self::parse_input_currency(&args.input_currency)?,
output_currencies: Self::parse_output_currency(&args.output_currency)?,
clean: args.clean,
integer: args.integer,
}
})
}
}

impl CliInput {
pub fn parse() -> Self {
Args::parse().into()
pub fn parse() -> Result<Self, InputError> {
Args::parse().try_into()
}

fn parse_amount(input: Option<String>) -> f64 {
fn parse_amount(input: Option<String>) -> Result<f64, InputError> {
match input {
Some(mut amount) => {
// check whether last character is an SI unit
Expand All @@ -98,41 +100,47 @@ impl CliInput {
}

match Self::strip_thousand_separators(&amount).parse::<f64>() {
Ok(amount) => amount * multiplier,
Err(_) => {
eprintln!("\"{}\" is not a valid amount!", amount);
process::exit(exitcode::USAGE);
}
Ok(amount) => Ok(amount * multiplier),
Err(_) => Err(InputError::new(&format!(
"\"{}\" is not a valid amount!",
amount
))),
}
}
None => Defaults::get_default_amount(),
None => Defaults::get_default_amount()
.map_err(|e| InputError::new(&format!("Failed to load default amount: {e}"))),
}
}

fn parse_input_currency(string: &Option<String>) -> Box<dyn Currency> {
fn parse_input_currency(string: &Option<String>) -> Result<Box<dyn Currency>, InputError> {
match string {
Some(currency) => match Currencies::parse(currency) {
Ok(currency) => currency,
Err(_) => {
eprintln!("\"{}\" is not a valid (input) currency!", currency);
process::exit(exitcode::USAGE);
}
Ok(currency) => Ok(currency),
Err(_) => Err(InputError::new(&format!(
"\"{}\" is not a valid (input) currency!",
currency
))),
},
None => Defaults::get_default_input_currency(),
None => Defaults::get_default_input_currency().map_err(|e| {
InputError::new(&format!("Failed to load default input currency: {e}"))
}),
}
}

fn parse_output_currency(string: &Option<String>) -> Vec<Box<dyn Currency>> {
fn parse_output_currency(
string: &Option<String>,
) -> Result<Vec<Box<dyn Currency>>, InputError> {
if let Some(string) = string {
match Currencies::parse(string) {
Ok(currency) => return vec![currency],
Ok(currency) => return Ok(vec![currency]),
Err(_) => {
eprintln!("\n{}\n", format!("\"{}\" is not a valid (output) currency! Showing multiple output currencies instead.", string).yellow());
}
}
}

Defaults::get_default_output_currencies()
.map_err(|e| InputError::new(&format!("Failed to load default output currencies: {e}")))
}

fn strip_thousand_separators(amount: &str) -> String {
Expand Down
41 changes: 20 additions & 21 deletions src/defaults.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use home_config::HomeConfig;
use serde::{Deserialize, Serialize};
use std::error::Error;
use std::process;

use crate::currency::btc::BitcoinUnit;
use crate::currency::fiat::Fiat;
Expand All @@ -17,39 +16,36 @@ pub struct Defaults {
}

impl Defaults {
pub fn get_default_amount() -> f64 {
Self::retrieve().amount
pub fn get_default_amount() -> Result<f64, Box<dyn Error>> {
Ok(Self::retrieve()?.amount)
}

pub fn get_default_input_currency() -> Box<dyn Currency> {
Self::retrieve().input_currency
pub fn get_default_input_currency() -> Result<Box<dyn Currency>, Box<dyn Error>> {
Ok(Self::retrieve()?.input_currency)
}

pub fn get_default_output_currencies() -> Vec<Box<dyn Currency>> {
Self::retrieve().output_currencies
pub fn get_default_output_currencies() -> Result<Vec<Box<dyn Currency>>, Box<dyn Error>> {
Ok(Self::retrieve()?.output_currencies)
}

pub fn retrieve() -> Defaults {
pub fn retrieve() -> Result<Defaults, Box<dyn Error>> {
let config = HomeConfig::with_config_dir(env!("CARGO_PKG_NAME"), DEFAULTS_FILE);

if !config.path().exists() {
log::debug!(
"{} does not exist. Creating it with template values.",
config.path().display()
);
Self::setup(&config);
Self::setup(&config)?;
}

match Self::load_defaults(&config) {
Ok(defaults) => defaults,
Err(err) => {
eprintln!(
"Can't load default values from file {}. Error: {}",
DEFAULTS_FILE, err
);
process::exit(exitcode::USAGE);
}
}
Self::load_defaults(&config).map_err(|err| {
format!(
"Can't load default values from file {}. Error: {}",
DEFAULTS_FILE, err
)
.into()
})
}

fn load_defaults(config: &HomeConfig) -> Result<Defaults, Box<dyn Error>> {
Expand All @@ -70,8 +66,11 @@ impl Defaults {
Ok(defaults)
}

fn setup(config: &HomeConfig) {
config.save_yaml(Self::load_defaults_template()).unwrap();
fn setup(config: &HomeConfig) -> Result<(), Box<dyn Error>> {
config
.save_yaml(Self::load_defaults_template())
.map_err(|e| format!("Failed to save default config: {e:?}"))?;
Ok(())
}

fn load_defaults_template() -> Defaults {
Expand Down
11 changes: 10 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ pub mod defaults;
pub mod fiat_rates;
mod print;

use std::process;

use colored::*;

use crate::cli_input::CliInput;
Expand All @@ -13,7 +15,14 @@ use crate::currency::Currency;
fn main() {
env_logger::init();

let cli_input = CliInput::parse();
let cli_input = match CliInput::parse() {
Ok(input) => input,
Err(e) => {
eprintln!("{e}");
process::exit(exitcode::USAGE);
}
};

let value_in_btc = cli_input.amount * cli_input.input_currency.btc_value();

if cli_input.output_currencies.len() == 1 {
Expand Down
Loading