aboutsummaryrefslogtreecommitdiffstats
path: root/src/ynab
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2019-08-18 23:45:33 -0400
committerJesse Luehrs <doy@tozt.net>2019-08-18 23:45:33 -0400
commit3539ceb20f5383a332a8ad1fcab816cf083f277e (patch)
tree5cea964a052c7c0bd967ede1d4283b75eba58ea5 /src/ynab
parentca8b49db6c369bc9b5c71a43ca7eb31140223cf1 (diff)
downloadynab-api-3539ceb20f5383a332a8ad1fcab816cf083f277e.tar.gz
ynab-api-3539ceb20f5383a332a8ad1fcab816cf083f277e.zip
better error handling
Diffstat (limited to 'src/ynab')
-rw-r--r--src/ynab/budget.rs66
-rw-r--r--src/ynab/client.rs65
2 files changed, 92 insertions, 39 deletions
diff --git a/src/ynab/budget.rs b/src/ynab/budget.rs
index b11e72a..e3275bb 100644
--- a/src/ynab/budget.rs
+++ b/src/ynab/budget.rs
@@ -1,3 +1,19 @@
+use snafu::{OptionExt, ResultExt};
+
+#[derive(Debug, snafu::Snafu)]
+pub enum Error {
+ #[snafu(display("couldn't get default budget: {}", source))]
+ GetBudget { source: super::client::Error },
+
+ #[snafu(display("couldn't update transactions: {}", source))]
+ UpdateTransactions { source: super::client::Error },
+
+ #[snafu(display("couldn't find the reimbursables category"))]
+ FindReimbursablesCategory,
+}
+
+pub type Result<T> = std::result::Result<T, Error>;
+
pub struct Budget {
client: super::client::Client,
id: String,
@@ -6,10 +22,10 @@ pub struct Budget {
}
impl Budget {
- pub fn new(key: &str) -> Self {
+ pub fn new(key: &str) -> Result<Self> {
let client = super::client::Client::new(key);
- let budget = client.default_budget();
- let reimbursables = Self::get_reimbursables(&budget);
+ let budget = client.default_budget().context(GetBudget)?;
+ let reimbursables = Self::get_reimbursables(&budget)?;
let budget = Self {
client,
id: budget.id.clone(),
@@ -17,15 +33,17 @@ impl Budget {
reimbursables,
};
budget.check();
- budget
+ Ok(budget)
}
- pub fn refresh(&mut self) {
- let budget = self.client.default_budget();
+ #[must_use]
+ pub fn refresh(&mut self) -> Result<()> {
+ let budget = self.client.default_budget().context(GetBudget)?;
self.id = budget.id.clone();
self.name = budget.name.clone();
- self.reimbursables = Self::get_reimbursables(&budget);
+ self.reimbursables = Self::get_reimbursables(&budget)?;
self.check();
+ Ok(())
}
pub fn name(&self) -> String {
@@ -40,10 +58,11 @@ impl Budget {
&self.reimbursables
}
+ #[must_use]
pub fn reconcile_transactions(
&self,
txns: &[&super::transaction::Transaction],
- ) -> Option<String> {
+ ) -> Result<()> {
let mut to_update =
ynab_api::models::UpdateTransactionsWrapper::new();
to_update.transactions = Some(
@@ -55,29 +74,31 @@ impl Budget {
})
.collect(),
);
- self.client.update_transactions(&self.id, to_update)
+ self.client
+ .update_transactions(&self.id, to_update)
+ .context(UpdateTransactions)?;
+ Ok(())
}
fn get_reimbursables(
budget: &ynab_api::models::BudgetDetail,
- ) -> Vec<super::transaction::Transaction> {
- let reimbursables_id = if let Some(categories) = &budget.categories {
- categories
- .iter()
- .find(|c| c.name == "Reimbursables")
- .map(|c| c.id.clone())
- .unwrap()
- } else {
- panic!("no categories found")
- };
+ ) -> Result<Vec<super::transaction::Transaction>> {
+ let reimbursables_id = budget
+ .categories
+ .as_ref()
+ .and_then(|categories| {
+ categories
+ .iter()
+ .find(|c| c.name == "Reimbursables")
+ .map(|c| c.id.clone())
+ })
+ .context(FindReimbursablesCategory)?;
let mut payee_map = std::collections::HashMap::new();
if let Some(payees) = &budget.payees {
for p in payees {
payee_map.insert(p.id.clone(), p.name.clone());
}
- } else {
- panic!("no payees?");
}
let payee_map = payee_map;
@@ -87,6 +108,7 @@ impl Budget {
account_map.insert(a.id.clone(), a.name.clone());
}
}
+ let account_map = account_map;
let mut reimbursables = vec![];
@@ -156,7 +178,7 @@ impl Budget {
}
reimbursables.sort_by_cached_key(|t| t.date.clone());
- reimbursables
+ Ok(reimbursables)
}
fn check(&self) {
diff --git a/src/ynab/client.rs b/src/ynab/client.rs
index 4b935c3..19b456b 100644
--- a/src/ynab/client.rs
+++ b/src/ynab/client.rs
@@ -1,3 +1,19 @@
+#[derive(Debug, snafu::Snafu)]
+pub enum Error {
+ // ynab-api error types don't implement Error, so can't use the
+ // auto-source behavior
+ #[snafu(display("failed to update transactions: {}", source_msg))]
+ UpdateTransactions { source_msg: String },
+
+ #[snafu(display("failed to get budgets: {}", source_msg))]
+ GetBudgets { source_msg: String },
+
+ #[snafu(display("failed to get budget {}: {}", id, source_msg))]
+ GetBudgetById { id: String, source_msg: String },
+}
+
+pub type Result<T> = std::result::Result<T, Error>;
+
pub struct Client {
api: ynab_api::apis::client::APIClient,
}
@@ -15,31 +31,46 @@ impl Client {
}
}
- pub fn default_budget(&self) -> ynab_api::models::BudgetDetail {
- let budgets =
- self.api.budgets_api().get_budgets().unwrap().data.budgets;
- let budget = budgets.iter().next().unwrap();
- self.api
+ pub fn default_budget(&self) -> Result<ynab_api::models::BudgetDetail> {
+ let budget_id = self
+ .api
.budgets_api()
- .get_budget_by_id(&budget.id, 0)
- .unwrap()
+ .get_budgets()
+ .map_err(|e| Error::GetBudgets {
+ source_msg: format!("{:?}", e),
+ })?
.data
- .budget
+ .budgets
+ .iter()
+ .next()
+ .ok_or_else(|| Error::GetBudgets {
+ source_msg: "no budgets found".to_string(),
+ })?
+ .id
+ .clone();
+ Ok(self
+ .api
+ .budgets_api()
+ .get_budget_by_id(&budget_id, 0)
+ .map_err(|e| Error::GetBudgetById {
+ id: budget_id.clone(),
+ source_msg: format!("{:?}", e),
+ })?
+ .data
+ .budget)
}
pub fn update_transactions(
&self,
budget_id: &str,
transactions: ynab_api::models::UpdateTransactionsWrapper,
- ) -> Option<String> {
- let res = self
- .api
+ ) -> Result<()> {
+ self.api
.transactions_api()
- .update_transactions(budget_id, transactions);
- if let Err(e) = res {
- Some(format!("{:?}", e))
- } else {
- None
- }
+ .update_transactions(budget_id, transactions)
+ .map(|_| ())
+ .map_err(|e| Error::UpdateTransactions {
+ source_msg: format!("{:?}", e),
+ })
}
}