use chrono::naive::NaiveDate; use rust_decimal::Decimal; use serde::{Deserialize, Serialize}; use std::convert::AsRef; use std::fmt; use std::fs; use std::hash::{Hash, Hasher}; use std::path::Path; use structopt::StructOpt; use tabled::Tabled; use twox_hash::XxHash64; pub(crate) type Account = String; pub(crate) type Category = String; #[derive(Debug)] #[derive(Hash)] #[derive(Deserialize, Serialize)] #[derive(StructOpt)] pub enum TransactionKind { Expense, Income, //TODO Transfer, } impl std::str::FromStr for TransactionKind { type Err = String; fn from_str(s: &str) -> Result { match s { "expense" => Ok(TransactionKind::Expense), "income" => Ok(TransactionKind::Income), _ => Err(format!("Unknown transaction kind: {:?}", s)), } } } impl fmt::Display for TransactionKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { TransactionKind::Expense => write!(f, "expense"), TransactionKind::Income => write!(f, "income"), } } } #[derive(Debug)] #[derive(Hash)] #[derive(Deserialize, Serialize)] #[derive(Tabled)] pub struct Transaction { pub description: String, pub date: NaiveDate, pub category: Category, pub amount: Decimal, pub kind: TransactionKind, pub from: Account, pub to: Account, } impl Transaction { pub(crate) fn write>(&self, p: &P) -> std::io::Result<()> { fs::write(p, serde_json::to_string_pretty(self).unwrap()) //TODO control pretty or not } //TODO Result pub(crate) fn open>(p: &P) -> Option { fs::read_to_string(p) .ok() .as_ref() .and_then(|s| serde_json::from_str(s).ok()) } pub fn id(&self) -> u64 { let mut h = XxHash64::default(); self.hash(&mut h); h.finish() } }