Skip to content

Commit

Permalink
fail gracefully when currencies cannot be converted in the roi command
Browse files Browse the repository at this point in the history
  • Loading branch information
frosklis committed Aug 29, 2021
1 parent cc52b5d commit d3dd373
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 27 deletions.
4 changes: 0 additions & 4 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -445,10 +445,6 @@ fn execute_command(
statistics::execute(&options, maybe_ledger)
}
} {
let err_str = format!("{}", e);
if !err_str.is_empty() {
eprintln!("{}", err_str);
}
return Err(e);
}
Ok(())
Expand Down
5 changes: 2 additions & 3 deletions src/commands/balance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@ pub fn execute(
flat: bool,
show_total: bool,
) -> Result<(), Box<dyn std::error::Error>> {
assert_eq!(
options.convert.is_some() & options.exchange.is_some(),
false,
assert!(
!(options.convert.is_some() && options.exchange.is_some()),
"Incompatible arguments --convert and --exchange"
);
let ledger = match maybe_ledger {
Expand Down
3 changes: 1 addition & 2 deletions src/commands/roi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,7 @@ pub fn execute(
let multipliers = conversion(currency.as_ref().unwrap().clone(), p.end, &ledger.prices);
p.final_money = Some(
convert_balance(&p.final_balance, &multipliers, currency.as_ref().unwrap())
.to_money()
.unwrap(),
.to_money()?,
);
}
if p.initial_money.is_none() {
Expand Down
39 changes: 38 additions & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use std::fmt;
use std::fmt::{Display, Formatter};
use std::path::PathBuf;

use crate::models::Balance;

#[derive(Debug)]
pub struct EmptyLedgerFileError;
impl Error for EmptyLedgerFileError {}
Expand Down Expand Up @@ -42,12 +44,47 @@ impl Display for TimeParseError {

#[derive(Debug)]
pub enum LedgerError {
TransactionIsNotBalanced,
EmptyPostingShouldBeLast,
AliasNotInList(String),
TooManyEmptyPostings(usize),
}
impl Error for LedgerError {}
impl Display for LedgerError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
LedgerError::EmptyPostingShouldBeLast => {
write!(f, "{}", "Empty posting should be last".red())
}
LedgerError::AliasNotInList(x) => write!(f, "Alias not found: {}", x),
LedgerError::TooManyEmptyPostings(_) => write!(f, "{}", "Too many empty postings".red()),
}
}
}
#[derive(Debug)]
pub enum BalanceError {
TransactionIsNotBalanced,
TooManyCurrencies(Balance),
}
impl Error for BalanceError {}

impl Display for BalanceError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
BalanceError::TransactionIsNotBalanced => {
write!(f, "{}", "Transaction is not balanced".red())
}

BalanceError::TooManyCurrencies(bal) => write!(
f,
"Too many currencies, probably a price is missing: {}",
bal.iter()
.map(|x| x.1.to_string())
.collect::<Vec<String>>()
.join(", ")
),
}
}
}
#[derive(Debug)]
pub struct GenericError {
pub message: Vec<ColoredString>,
Expand Down
12 changes: 3 additions & 9 deletions src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ impl ParsedLedger {
/// 5. Checks whether transactions are balanced again
///
/// There may be room for optimization here
pub fn to_ledger(mut self, options: &CommonOpts) -> Result<Ledger, GenericError> {
pub fn to_ledger(mut self, options: &CommonOpts) -> Result<Ledger, Box<dyn std::error::Error>> {
let mut commodity_strs = HashSet::<String>::new();
let mut account_strs = HashSet::<String>::new();
let mut payee_strs = HashSet::<String>::new();
Expand Down Expand Up @@ -211,13 +211,7 @@ impl ParsedLedger {
for t in transactions.iter_mut() {
let date = t.date.unwrap();
// output_balances(&balances);
let balance = match t.balance(&mut balances, options.no_balance_check) {
Ok(balance) => balance,
Err(e) => {
eprintln!("{}", t);
return Err(e.into());
}
};
let balance = t.balance(&mut balances, options.no_balance_check)?;
if balance.len() == 2 {
let vec = balance.iter().map(|(_, x)| x.abs()).collect::<Vec<Money>>();

Expand Down Expand Up @@ -350,7 +344,7 @@ impl ParsedLedger {
Ok(balance) => balance,
Err(e) => {
eprintln!("{}", t);
return Err(e.into());
return Err(e);
}
};
if balance.len() == 2 {
Expand Down
6 changes: 3 additions & 3 deletions src/models/balance.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::error::LedgerError;
use crate::error::BalanceError;
use crate::models::{Currency, Money};
use std::collections::hash_map::Iter;
use std::collections::HashMap;
Expand Down Expand Up @@ -26,7 +26,7 @@ impl Balance {

/// Automatic conversion from balance to regular money
/// it can only be done if the balance has only one currency
pub fn to_money(&self) -> Result<Money, LedgerError> {
pub fn to_money(&self) -> Result<Money, BalanceError> {
let vec = self
.balance
.values()
Expand All @@ -35,7 +35,7 @@ impl Balance {
match vec.len() {
0 => Ok(Money::Zero),
1 => Ok(vec[0].clone()),
_ => Err(LedgerError::TransactionIsNotBalanced),
_ => Err(BalanceError::TooManyCurrencies(self.clone())),
}
}
pub fn is_zero(&self) -> bool {
Expand Down
11 changes: 6 additions & 5 deletions src/models/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::rc::{Rc, Weak};
use chrono::NaiveDate;
use num::rational::BigRational;

use crate::error::BalanceError;
use crate::models::balance::Balance;
use crate::models::{Account, Comment, HasName, Money, Payee};
use crate::{LedgerError, List};
Expand Down Expand Up @@ -274,14 +275,14 @@ impl Transaction<Posting> {
&mut self,
balances: &mut HashMap<Rc<Account>, Balance>,
skip_balance_check: bool,
) -> Result<Balance, LedgerError> {
) -> Result<Balance, Box<dyn std::error::Error>> {
let mut transaction_balance = Balance::new();

// 1. Check the virtual postings
match total_balance(&*self.postings.borrow(), PostingType::VirtualMustBalance).can_be_zero()
{
true => {}
false => return Err(LedgerError::TransactionIsNotBalanced),
false => return Err(Box::new(BalanceError::TransactionIsNotBalanced)),
}

// 1. Iterate over postings
Expand All @@ -307,7 +308,7 @@ impl Transaction<Posting> {
"Difference: {}",
expected_balance - Balance::from(balance.clone())
);
return Err(LedgerError::TransactionIsNotBalanced);
return Err(Box::new(BalanceError::TransactionIsNotBalanced));
}
}
}
Expand Down Expand Up @@ -403,7 +404,7 @@ impl Transaction<Posting> {
.count()
- postings.len();
if empties > 1 {
Err(LedgerError::TooManyEmptyPostings(empties))
Err(Box::new(LedgerError::TooManyEmptyPostings(empties)))
} else if empties == 0 {
match transaction_balance.can_be_zero() {
true => {
Expand All @@ -420,7 +421,7 @@ impl Transaction<Posting> {
self.postings.replace(postings);
Ok(transaction_balance)
}
false => Err(LedgerError::TransactionIsNotBalanced),
false => Err(Box::new(BalanceError::TransactionIsNotBalanced)),
}
} else {
// Fill the empty posting
Expand Down

0 comments on commit d3dd373

Please sign in to comment.