use chrono::{naive::NaiveDate, Duration}; use rust_decimal::Decimal; use std::path::PathBuf; use std::str::FromStr; use structopt::StructOpt; use tabled::{Style, Table}; mod search; mod store; mod transaction; use search::{Constraint, DateFilter, Search}; use store::Store; use transaction::{Transaction, TransactionKind}; //TODO relative ("yesterday", "-2d", etc) fn parse_date(s: &str) -> Result { NaiveDate::parse_from_str(s, "%Y-%m-%d").map_err(|e| e.to_string()) } #[derive(Debug)] #[derive(StructOpt)] enum Command { Insert { kind: TransactionKind, #[structopt(long)] account: String, // #[structopt(long = "category", multiple = true, number_of_values = 1)] // category: Vec, #[structopt(long)] category: String, #[structopt(long, parse(try_from_str = Decimal::from_str))] amount: Decimal, description: String, #[structopt(long, parse(try_from_str = parse_date))] date: Option, }, List { target: ListTarget, }, Show, } #[derive(Debug)] #[derive(StructOpt)] enum ListTarget { Categories, } impl std::str::FromStr for ListTarget { type Err = String; fn from_str(s: &str) -> Result { match s { "categories" => Ok(ListTarget::Categories), _ => Err(format!("Unknown listable: {:?}", s)), } } } #[derive(Debug)] #[derive(StructOpt)] struct Mn { #[structopt(subcommand)] command: Command, } fn main() { let mut store = Store::open(PathBuf::from("store")).unwrap(); let search = Search::new(store.transactions()); // let search = search.subtract(Constraint::Category("a".to_string())); let search = search.subtract(Constraint::Date(DateFilter::Relative { start: None, end: Some(Duration::days(-2)), })); let args = Mn::from_args(); eprintln!("{:?}", args); match args.command { Command::Insert { kind, account, category, amount, description, date, } => { let transaction = Transaction { kind, to: account, from: "Default".to_string(), category, amount, description, date: match date { Some(date) => date, None => chrono::offset::Local::today().naive_utc(), }, }; eprintln!("{:?}", transaction); println!("{}", transaction.id()); store.push(transaction); store.write().unwrap(); } Command::List { target: ListTarget::Categories } => { println!("{}", store.categories().join("\n")); } Command::Show => { let mut transactions = search.get(); transactions.sort_by(|t1, t2| t1.date.cmp(&t2.date)); println!("{}", Table::new(transactions).with(Style::psql())); } } }