From 415a1f86849d3bff8c52ea0c7397978dacfc0975 Mon Sep 17 00:00:00 2001 From: James McDonald Date: Mon, 25 May 2026 23:15:20 +0200 Subject: [PATCH] Make number of days configurable for transactions --- src/lib.rs | 3 +++ src/main.rs | 18 ++++++++++++------ src/ynab/client.rs | 15 ++++++++++++--- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f050fa3..d7e1591 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,6 +24,9 @@ pub enum Error { AmountEmpty, #[error("bank format not found: {0}")] BankFormat(String), + #[error("API error {status}: {message}")] + ApiError { status: u16, message: String }, + /// Errors I've been too lazy to give a type yet #[error("{0}")] Text(String), diff --git a/src/main.rs b/src/main.rs index 5e87a17..a52a82c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,10 +26,11 @@ enum Command { #[arg(short = 'P', long, group = "planref")] plan_id: Option, }, - // List transactions in the last 30 days - // - // You have to give a plan to list transactions from. You can optionally - // also give an account to show only transactions from that account. + /// List transactions + /// + /// You have to give a plan to list transactions from. You can optionally + /// also give an account to show only transactions from that account. You can specify the + /// number of days, the default is 30. Transactions { /// Your YNAB token, available from developer settings in YNAB #[arg(short, long, env = "YNAB_API_TOKEN", hide_env_values = true)] @@ -50,6 +51,10 @@ enum Command { /// Alternatively, give the YNAB account ID directly #[arg(short = 'A', long, group = "accountref")] account_id: Option, + + /// The number of days to look back for transactions + #[arg(short, long, default_value_t = 30)] + days: i64, }, /// Convert from a bank export to a CSV you can import manually to YNAB Convert { @@ -135,6 +140,7 @@ async fn main() -> anyhow::Result<()> { plan_id, account, account_id, + days, } => { let client = Client::new(&token); let plan = match (plan, plan_id) { @@ -151,8 +157,8 @@ async fn main() -> anyhow::Result<()> { }; let transactions = match account { - Some(account) => account.list_transactions().await?, - None => plan.list_transactions(None).await?, + Some(account) => account.list_transactions(days).await?, + None => plan.list_transactions(None, days).await?, }; println!("Transactions{} in the last 30 days:", accountstr); for t in transactions { diff --git a/src/ynab/client.rs b/src/ynab/client.rs index 9b61c27..c9daa75 100644 --- a/src/ynab/client.rs +++ b/src/ynab/client.rs @@ -26,6 +26,14 @@ impl Client { .bearer_auth(&self.token) .send() .await?; + if !res.status().is_success() { + let code = res.status().as_u16(); + let message = res.status().canonical_reason().unwrap_or("unknown"); + return Err(Error::ApiError { + status: code, + message: message.to_string(), + }); + } let text = res.text().await?; Ok(text) } @@ -121,8 +129,9 @@ impl Plan { pub async fn list_transactions( &self, account_id: Option<&str>, + days: i64, ) -> Result, Error> { - let since_date = chrono::Local::now().date_naive() - chrono::Duration::days(30); + let since_date = chrono::Local::now().date_naive() - chrono::Duration::days(days); let since_date = since_date.format("%Y-%m-%d"); let url = format!("plans/{}/transactions?since_date={}", self.id, since_date); let raw = self.client.get(&url).await?; @@ -166,8 +175,8 @@ impl Account { }) } - pub async fn list_transactions(&self) -> Result, Error> { - self.plan.list_transactions(Some(&self.id)).await + pub async fn list_transactions(&self, days: i64) -> Result, Error> { + self.plan.list_transactions(Some(&self.id), days).await } pub async fn upload(&self, transactions: &[Transaction]) -> Result<(), Error> {