Start refactor of ynab by splitting it up
This commit is contained in:
+3
-3
@@ -115,7 +115,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
plan,
|
plan,
|
||||||
plan_id,
|
plan_id,
|
||||||
} => {
|
} => {
|
||||||
let plan = Lookup::try_from((plan, plan_id))?;
|
let plan = Lookup::from_options(plan, plan_id)?;
|
||||||
let accounts = Ynab::new(&token).list_accounts(plan)?;
|
let accounts = Ynab::new(&token).list_accounts(plan)?;
|
||||||
println!("Available accounts in plan:");
|
println!("Available accounts in plan:");
|
||||||
for account in accounts {
|
for account in accounts {
|
||||||
@@ -132,8 +132,8 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
format,
|
format,
|
||||||
inputs,
|
inputs,
|
||||||
} => {
|
} => {
|
||||||
let plan = Lookup::try_from((plan, plan_id))?;
|
let plan = Lookup::from_options(plan, plan_id)?;
|
||||||
let account = Lookup::try_from((account, account_id))?;
|
let account = Lookup::from_options(account, account_id)?;
|
||||||
|
|
||||||
let transactions = read_transactions_from(&inputs, &format)?;
|
let transactions = read_transactions_from(&inputs, &format)?;
|
||||||
|
|
||||||
|
|||||||
@@ -1,65 +1,7 @@
|
|||||||
|
use super::lookup::Lookup;
|
||||||
|
use super::transform;
|
||||||
|
use super::types;
|
||||||
use crate::Transaction;
|
use crate::Transaction;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
struct YnabTransaction {
|
|
||||||
import_id: String,
|
|
||||||
date: String,
|
|
||||||
amount: i64,
|
|
||||||
payee_name: String,
|
|
||||||
account_id: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
struct YnabResponse<T> {
|
|
||||||
data: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
struct YnabPlanList {
|
|
||||||
plans: Vec<YnabPlan>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
struct YnabPlan {
|
|
||||||
id: String,
|
|
||||||
name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
struct YnabAccountList {
|
|
||||||
accounts: Vec<YnabAccount>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
struct YnabAccount {
|
|
||||||
id: String,
|
|
||||||
name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
|
||||||
struct YnabTransactionList {
|
|
||||||
transactions: Vec<YnabTransaction>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Lookup {
|
|
||||||
Name(String),
|
|
||||||
Id(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<(Option<String>, Option<String>)> for Lookup {
|
|
||||||
type Error = String;
|
|
||||||
|
|
||||||
fn try_from((name, id): (Option<String>, Option<String>)) -> Result<Self, Self::Error> {
|
|
||||||
match (name, id) {
|
|
||||||
(Some(name), _) => Ok(Lookup::Name(name)),
|
|
||||||
(_, Some(id)) => Ok(Lookup::Id(id)),
|
|
||||||
_ => Err("must provide name or id".to_owned()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Ynab {
|
pub struct Ynab {
|
||||||
token: String,
|
token: String,
|
||||||
@@ -79,7 +21,7 @@ impl Ynab {
|
|||||||
Lookup::Id(id) => Ok(id),
|
Lookup::Id(id) => Ok(id),
|
||||||
Lookup::Name(name) => {
|
Lookup::Name(name) => {
|
||||||
let url = "plans";
|
let url = "plans";
|
||||||
let res: YnabResponse<YnabPlanList> = serde_json::from_str(
|
let res: types::YnabResponse<types::YnabPlanList> = serde_json::from_str(
|
||||||
&self.get(url).map_err(|e| format!("request error: {}", e))?,
|
&self.get(url).map_err(|e| format!("request error: {}", e))?,
|
||||||
)
|
)
|
||||||
.map_err(|e| format!("parse error: {}", e))?;
|
.map_err(|e| format!("parse error: {}", e))?;
|
||||||
@@ -98,7 +40,7 @@ impl Ynab {
|
|||||||
Lookup::Id(id) => Ok(id),
|
Lookup::Id(id) => Ok(id),
|
||||||
Lookup::Name(name) => {
|
Lookup::Name(name) => {
|
||||||
let url = format!("plans/{}/accounts", plan_id);
|
let url = format!("plans/{}/accounts", plan_id);
|
||||||
let res: YnabResponse<YnabAccountList> = serde_json::from_str(
|
let res: types::YnabResponse<types::YnabAccountList> = serde_json::from_str(
|
||||||
&self
|
&self
|
||||||
.get(&url)
|
.get(&url)
|
||||||
.map_err(|e| format!("request error: {}", e))?,
|
.map_err(|e| format!("request error: {}", e))?,
|
||||||
@@ -114,7 +56,7 @@ impl Ynab {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, request: &str) -> Result<String, reqwest::Error> {
|
pub(crate) fn get(&self, request: &str) -> Result<String, reqwest::Error> {
|
||||||
const URL: &str = "https://api.ynab.com/v1/";
|
const URL: &str = "https://api.ynab.com/v1/";
|
||||||
let request = format!("{}{}", URL, request);
|
let request = format!("{}{}", URL, request);
|
||||||
let res = self
|
let res = self
|
||||||
@@ -151,7 +93,7 @@ impl Ynab {
|
|||||||
|
|
||||||
pub fn list_plans(&self) -> Result<Vec<String>, String> {
|
pub fn list_plans(&self) -> Result<Vec<String>, String> {
|
||||||
let url = String::from("plans");
|
let url = String::from("plans");
|
||||||
let res: YnabResponse<YnabPlanList> = serde_json::from_str(
|
let res: types::YnabResponse<types::YnabPlanList> = serde_json::from_str(
|
||||||
&self
|
&self
|
||||||
.get(&url)
|
.get(&url)
|
||||||
.map_err(|e| format!("request error: {}", e))?,
|
.map_err(|e| format!("request error: {}", e))?,
|
||||||
@@ -163,7 +105,7 @@ impl Ynab {
|
|||||||
pub fn list_accounts(&self, plan: Lookup) -> Result<Vec<String>, String> {
|
pub fn list_accounts(&self, plan: Lookup) -> Result<Vec<String>, String> {
|
||||||
let plan_id = self.resolve_plan(plan)?;
|
let plan_id = self.resolve_plan(plan)?;
|
||||||
let url = format!("plans/{}/accounts", plan_id);
|
let url = format!("plans/{}/accounts", plan_id);
|
||||||
let res: YnabResponse<YnabAccountList> = serde_json::from_str(
|
let res: types::YnabResponse<types::YnabAccountList> = serde_json::from_str(
|
||||||
&self
|
&self
|
||||||
.get(&url)
|
.get(&url)
|
||||||
.map_err(|e| format!("request error: {}", e))?,
|
.map_err(|e| format!("request error: {}", e))?,
|
||||||
@@ -172,32 +114,6 @@ impl Ynab {
|
|||||||
Ok(res.data.accounts.into_iter().map(|p| p.name).collect())
|
Ok(res.data.accounts.into_iter().map(|p| p.name).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ynab_transactions(
|
|
||||||
&self,
|
|
||||||
transactions: &[Transaction],
|
|
||||||
account_id: &str,
|
|
||||||
) -> Vec<YnabTransaction> {
|
|
||||||
let mut result = Vec::new();
|
|
||||||
let mut idmap: HashMap<(String, i64), u32> = HashMap::new();
|
|
||||||
for t in transactions {
|
|
||||||
let key = (t.date.clone(), t.amount);
|
|
||||||
let id = {
|
|
||||||
let n = idmap.entry(key).or_insert(0);
|
|
||||||
*n += 1;
|
|
||||||
*n
|
|
||||||
};
|
|
||||||
let y = YnabTransaction {
|
|
||||||
import_id: format!("YNAB:{}:{}:{}", t.amount, t.date, id),
|
|
||||||
date: t.date.clone(),
|
|
||||||
amount: t.amount,
|
|
||||||
payee_name: t.payee.clone(),
|
|
||||||
account_id: account_id.to_string(),
|
|
||||||
};
|
|
||||||
result.push(y);
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn upload(
|
pub fn upload(
|
||||||
&self,
|
&self,
|
||||||
transactions: &[Transaction],
|
transactions: &[Transaction],
|
||||||
@@ -207,8 +123,8 @@ impl Ynab {
|
|||||||
let plan_id = self.resolve_plan(plan)?;
|
let plan_id = self.resolve_plan(plan)?;
|
||||||
let account_id = self.resolve_account(&plan_id, account)?;
|
let account_id = self.resolve_account(&plan_id, account)?;
|
||||||
let request = format!("plans/{}/transactions", plan_id);
|
let request = format!("plans/{}/transactions", plan_id);
|
||||||
let body = serde_json::to_string(&YnabTransactionList {
|
let body = serde_json::to_string(&types::YnabTransactionList {
|
||||||
transactions: self.ynab_transactions(transactions, &account_id),
|
transactions: transform::ynab_transactions(transactions, &account_id),
|
||||||
})
|
})
|
||||||
.map_err(|e| format!("transaction format error: {}", e))?;
|
.map_err(|e| format!("transaction format error: {}", e))?;
|
||||||
println!("{}", body);
|
println!("{}", body);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
pub enum Lookup {
|
||||||
|
Name(String),
|
||||||
|
Id(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Lookup {
|
||||||
|
pub fn from_options(name: Option<String>, id: Option<String>) -> Result<Self, String> {
|
||||||
|
match (name, id) {
|
||||||
|
(Some(name), _) => Ok(Self::Name(name)),
|
||||||
|
(_, Some(id)) => Ok(Self::Id(id)),
|
||||||
|
_ => Err("must provide name or id".to_owned()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
mod lookup;
|
||||||
|
pub use lookup::Lookup;
|
||||||
|
|
||||||
|
mod types;
|
||||||
|
|
||||||
|
mod client;
|
||||||
|
pub use client::Ynab;
|
||||||
|
|
||||||
|
mod transform;
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
use super::types;
|
||||||
|
use crate::Transaction;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub(crate) fn ynab_transactions(
|
||||||
|
transactions: &[Transaction],
|
||||||
|
account_id: &str,
|
||||||
|
) -> Vec<types::YnabTransaction> {
|
||||||
|
let mut result = Vec::new();
|
||||||
|
let mut idmap: HashMap<(String, i64), u32> = HashMap::new();
|
||||||
|
for t in transactions {
|
||||||
|
let key = (t.date.clone(), t.amount);
|
||||||
|
let id = {
|
||||||
|
let n = idmap.entry(key).or_insert(0);
|
||||||
|
*n += 1;
|
||||||
|
*n
|
||||||
|
};
|
||||||
|
let y = types::YnabTransaction {
|
||||||
|
import_id: format!("YNAB:{}:{}:{}", t.amount, t.date, id),
|
||||||
|
date: t.date.clone(),
|
||||||
|
amount: t.amount,
|
||||||
|
payee_name: t.payee.clone(),
|
||||||
|
account_id: account_id.to_string(),
|
||||||
|
};
|
||||||
|
result.push(y);
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub(crate) struct YnabTransaction {
|
||||||
|
pub(crate) import_id: String,
|
||||||
|
pub(crate) date: String,
|
||||||
|
pub(crate) amount: i64,
|
||||||
|
pub(crate) payee_name: String,
|
||||||
|
pub(crate) account_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub(crate) struct YnabResponse<T> {
|
||||||
|
pub(crate) data: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub(crate) struct YnabPlanList {
|
||||||
|
pub(crate) plans: Vec<YnabPlan>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub(crate) struct YnabPlan {
|
||||||
|
pub(crate) id: String,
|
||||||
|
pub(crate) name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub(crate) struct YnabAccountList {
|
||||||
|
pub(crate) accounts: Vec<YnabAccount>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub(crate) struct YnabAccount {
|
||||||
|
pub(crate) id: String,
|
||||||
|
pub(crate) name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug)]
|
||||||
|
pub(crate) struct YnabTransactionList {
|
||||||
|
pub(crate) transactions: Vec<YnabTransaction>,
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user