From 69a1814ec65bef29f31e6f3c514177300f1e1566 Mon Sep 17 00:00:00 2001 From: Gabriel Comte Date: Mon, 30 Mar 2026 09:11:24 +0200 Subject: [PATCH] Replace process::exit() with Result propagation Return Result from CliInput::parse() and Defaults::retrieve() instead of calling process::exit() directly. Errors are now handled in main(). --- src/cli_input.rs | 60 +++++++++++++++++++++++++++--------------------- src/defaults.rs | 41 ++++++++++++++++----------------- src/main.rs | 11 ++++++++- 3 files changed, 64 insertions(+), 48 deletions(-) diff --git a/src/cli_input.rs b/src/cli_input.rs index cfb0af3..e809457 100644 --- a/src/cli_input.rs +++ b/src/cli_input.rs @@ -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; @@ -66,24 +66,26 @@ impl From for InputError { } } -impl From 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 for CliInput { + type Error = InputError; + + fn try_from(args: Args) -> Result { + 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 { + Args::parse().try_into() } - fn parse_amount(input: Option) -> f64 { + fn parse_amount(input: Option) -> Result { match input { Some(mut amount) => { // check whether last character is an SI unit @@ -98,34 +100,39 @@ impl CliInput { } match Self::strip_thousand_separators(&amount).parse::() { - 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) -> Box { + fn parse_input_currency(string: &Option) -> Result, 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) -> Vec> { + fn parse_output_currency( + string: &Option, + ) -> Result>, 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()); } @@ -133,6 +140,7 @@ impl CliInput { } 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 { diff --git a/src/defaults.rs b/src/defaults.rs index f252e25..0cd4071 100644 --- a/src/defaults.rs +++ b/src/defaults.rs @@ -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; @@ -17,19 +16,19 @@ pub struct Defaults { } impl Defaults { - pub fn get_default_amount() -> f64 { - Self::retrieve().amount + pub fn get_default_amount() -> Result> { + Ok(Self::retrieve()?.amount) } - pub fn get_default_input_currency() -> Box { - Self::retrieve().input_currency + pub fn get_default_input_currency() -> Result, Box> { + Ok(Self::retrieve()?.input_currency) } - pub fn get_default_output_currencies() -> Vec> { - Self::retrieve().output_currencies + pub fn get_default_output_currencies() -> Result>, Box> { + Ok(Self::retrieve()?.output_currencies) } - pub fn retrieve() -> Defaults { + pub fn retrieve() -> Result> { let config = HomeConfig::with_config_dir(env!("CARGO_PKG_NAME"), DEFAULTS_FILE); if !config.path().exists() { @@ -37,19 +36,16 @@ impl Defaults { "{} 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> { @@ -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> { + config + .save_yaml(Self::load_defaults_template()) + .map_err(|e| format!("Failed to save default config: {e:?}"))?; + Ok(()) } fn load_defaults_template() -> Defaults { diff --git a/src/main.rs b/src/main.rs index 42ed9ac..392d16e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,8 @@ pub mod defaults; pub mod fiat_rates; mod print; +use std::process; + use colored::*; use crate::cli_input::CliInput; @@ -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 {