From a5dfd2627853bd67e9d17ef870fc84be312b1397 Mon Sep 17 00:00:00 2001 From: jvech Date: Tue, 23 Apr 2024 07:52:28 -0500 Subject: add: first commit done --- src/cli.rs | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/database.rs | 68 ++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 48 ++++++++++++++++++++++++++++++++++ 3 files changed, 197 insertions(+) create mode 100644 src/cli.rs create mode 100644 src/database.rs create mode 100644 src/main.rs (limited to 'src') diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..0bd0a78 --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,81 @@ +use std::path::PathBuf; +use clap::{Subcommand, Parser, ValueHint}; +use std::env; + +#[derive(Debug, Parser)] +#[command(version, about, long_about = None)] +pub struct Cli { + /// file path where data is stored [default: + /// $XDG_DATA_HOME/debt/debt.db + #[arg(short, long, env="DEBT_DB", value_hint = ValueHint::FilePath)] + pub database_path: Option, + + #[command(subcommand)] + pub action: Commands +} + +#[derive(Debug, Subcommand)] +pub enum Commands { + /// Initialize Database + Init, + /// Register a transaction + Register { + /// person to register the transaction + person: String, + + /// amount of money + amount: i32, + + /// additional notes + note: Option, + + /// Register the transaction as a payment + #[arg(short, long)] + payment: bool + }, + + View { + filter: Option, + /// View register history + #[arg(short='H', long)] + history: bool, + /// Transaction accumulation by person + #[arg(short, long)] + total: bool + }, + + /// Add a new agent to lend or pay + Add { + name: String, + }, +} + +impl Cli { + pub fn new() -> Self { + let mut cli = Cli::parse(); + load_datapath(&mut cli); + cli + } +} + +fn load_datapath(cli: &mut Cli) { + let config_path = cli + .database_path + .clone() + .or_else(|| { + xdg::BaseDirectories::with_prefix("debt") + .ok() + .and_then(|x| { + x.find_data_file("cache.db") + }) + }).or_else(|| { + if let Ok(home) = env::var("HOME") { + let fallback = PathBuf::from(&home).join(".debt.db"); + if fallback.exists() { + return Some(fallback) + } + } + None + }); + cli.database_path = config_path; +} diff --git a/src/database.rs b/src/database.rs new file mode 100644 index 0000000..6a7bde0 --- /dev/null +++ b/src/database.rs @@ -0,0 +1,68 @@ +use rusqlite::Connection; +use rusqlite::Result; +use chrono::Utc; +use std::path::PathBuf; + +pub struct Database { + date: i64, + person: String, + amount: i32, + note: Option, +} + +impl Database { + pub fn new(person: String, amount: i32, note: Option, is_debt: bool) -> Database { + Database { + date: Utc::now().timestamp(), + amount: if !is_debt {amount} else {-amount}, + person, + note, + } + } + + pub fn add_register(&self, filepath: &PathBuf) -> Result<()> { + let conn = Connection::open(filepath)?; + conn.execute("PRAGMA foreign_key=1", ())?; + conn.execute(" + INSERT INTO Registers (agent_id, register_date, amount, note) + VALUES ( + (SELECT id FROM Agents WHERE name=?1), + ?2, ?3, ?4)", + (&self.person, + &self.date, + self.amount, + &self.note))?; + println!("Register added successfully"); + Ok(()) + } + + pub fn add_agent(&self, filepath: &PathBuf) -> Result<()> { + let conn = Connection::open(filepath)?; + conn.execute(" + INSERT INTO Agents (name) + VALUES (?1)", (&self.person,))?; + println!("agent '{}' successfully", &self.person); + Ok(()) + } + + pub fn init_database(filepath: &PathBuf) -> Result<()> { + let conn = Connection::open(filepath).expect("Database file creation Error"); + conn.execute(" + CREATE TABLE Agents ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL UNIQUE)", ()) + .expect("SQL initialization error"); + + conn.execute(" + CREATE TABLE Registers ( + id INTEGER PRIMARY KEY, + agent_id INTEGER, + register_date INTEGER NOT NULL, + amount INTEGER NOT NULL, + note TEXT, + FOREIGN KEY(agent_id) REFERENCES Agents(id))", ()) + .expect("SQL initialization error"); + println!("'{}' database created", &filepath.display()); + Ok(()) + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..8ef62b3 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,48 @@ +use xdg; +use rusqlite::Result; + +mod database; +mod cli; + +use cli::{Cli, Commands}; +use crate::database::Database; + +fn main() -> Result<()> { + let cli = Cli::new(); + match cli.action { + Commands::Init => { + if let Some(datapath) = cli.database_path { + Database::init_database(&datapath)?; + } else { + let datapath = xdg::BaseDirectories::with_prefix("debt") + .expect("xdg file setup failed") + .place_data_file("cache.db") + .expect("xdg directory creaton failed"); + Database::init_database(&datapath)?; + } + }, + Commands::Register {person, amount, note, payment} => { + let register = Database::new(person, amount, note, !payment); + if let Some(datapath) = cli.database_path { + register.add_register(&datapath)?; + } else { + panic!("database file not found"); + } + }, + Commands::View {history, filter, total} => { + println!("{}", filter.unwrap_or(String::from("None"))); + println!("{}", history); + println!("{}", total); + }, + + Commands::Add {name} => { + let register = Database::new(name, 0, None, false); + if let Some(datapath) = cli.database_path { + register.add_agent(&datapath)?; + } else { + panic!("database file not found"); + } + } + }; + Ok(()) +} -- cgit v1.2.3-70-g09d2