diff options
Diffstat (limited to 'cli/src/model.rs')
| -rw-r--r-- | cli/src/model.rs | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/cli/src/model.rs b/cli/src/model.rs new file mode 100644 index 0000000..b0dab7a --- /dev/null +++ b/cli/src/model.rs @@ -0,0 +1,106 @@ +use rust_decimal::Decimal; +use serde::{Deserialize, Serialize}; +use std::convert::AsRef; +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)] +pub enum TransactionKind { + Expense, + Income, + //TODO Transfer, +} + +#[derive(Debug)] +#[derive(Hash)] +#[derive(Deserialize, Serialize)] +pub struct Transaction { + description: String, + category: Category, + amount: Decimal, + kind: TransactionKind, + from: Account, + to: Account, +} + +#[derive(Debug)] +#[derive(Hash)] +#[derive(Deserialize, Serialize)] +pub struct Post { + transaction: Transaction, + removed: bool, +} + +#[derive(Debug)] +pub struct Store { + root: PathBuf, + posts: Vec<Post>, +} + +impl Store { + //TODO Result + pub fn open(root: PathBuf) -> Option<Self> { + 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<Vec<Post>> { + 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) + } + + pub 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 { + pub fn new(transaction: Transaction) -> Self { + Self { + transaction, + removed: false, + } + } + + fn write<P: AsRef<Path>>(&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: AsRef<Path>>(p: &P) -> Option<Self> { + 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() + } +} + |
