use rust_decimal::Decimal; use serde::{Deserialize, Serialize}; use std::convert::{AsRef, TryFrom}; use std::fs; use std::hash::{Hash, Hasher}; use std::path::{Path, PathBuf}; use twox_hash::XxHash64; type Account = String; type Category = String; #[derive(Debug)] #[derive(Hash)] #[derive(Deserialize, Serialize)] enum TransactionKind { Expense, Income, //TODO Transfer, } #[derive(Debug)] #[derive(Hash)] #[derive(Deserialize, Serialize)] struct Transaction { description: String, category: Category, amount: Decimal, kind: TransactionKind, from: Account, to: Account, } #[derive(Debug)] #[derive(Hash)] #[derive(Deserialize, Serialize)] struct Post { transaction: Transaction, removed: bool, } #[derive(Debug)] struct Store { root: PathBuf, posts: Vec, } impl Store { //TODO Result fn open(root: PathBuf) -> Option { Some(Self { posts: Self::open_dir(&root)?, root, }) } //TODO check if hash matches //TODO Result //TODO overkill? maybe we can use subfolders later on fn open_dir(dir: &Path) -> Option> { let mut res = Vec::new(); for entry in std::fs::read_dir(dir).ok()? { let entry = entry.ok()?; if entry.file_type().ok()?.is_dir() { let mut posts = Self::open_dir(&entry.path())?; res.append(&mut posts); } else { res.push(Post::open(&entry.path())?); } } Some(res) } fn write(&self) -> std::io::Result<()> { for post in &self.posts { let mut path = self.root.clone(); path.push(format!("{}", post.id())); post.write(&path)?; } Ok(()) } } impl Post { fn new(transaction: Transaction) -> Self { Self { transaction, removed: false, } } 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 fn open>(p: &P) -> Option { fs::read_to_string(p).ok().as_ref().and_then(|s| serde_json::from_str(s).ok()) } fn id(&self) -> u64 { let mut h = XxHash64::default(); self.hash(&mut h); h.finish() } } fn main() { // let post = Post::new(Transaction { // description: "Test".to_string(), // category: "Mat".to_string(), // amount: Decimal::try_from(120.4_f64).unwrap(), // kind: TransactionKind::Expense, // from: "Kortkonto".to_string(), // to: "Coop Lambohov".to_string(), // }); // post.write(&"test").unwrap(); let store = Store::open(PathBuf::from("store")).unwrap(); // println!("{:#?}", store); store.write().unwrap(); }