From 9af29da716ea3835357b83822cb53c94abac2001 Mon Sep 17 00:00:00 2001 From: alemidev Date: Mon, 28 Mar 2022 01:52:05 +0200 Subject: [PATCH] improved file lookup, load by extension --- src/main.rs | 66 +++++++++++++++++++++++++++++--------------------- src/storage.rs | 3 ++- src/utils.rs | 20 +++++++++++++++ 3 files changed, 60 insertions(+), 29 deletions(-) diff --git a/src/main.rs b/src/main.rs index 62baa55..51b7619 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,15 +10,15 @@ use git_version::git_version; use notify_rust::Notification; use regex::Regex; use remote::RemoteSync; -use storage::{AuthStorage, Memo, MemoStorage, SQLiteStorage, StateStorage, JsonStorage}; -use utils::{find_by_regex, parse_human_duration, HumanDisplay}; +use storage::{AuthStorage, Memo, MemoError, MemoStorage, SQLiteStorage, StateStorage, JsonStorage, SUPPORTED_FORMATS}; +use utils::{find_by_regex, find_db_file, parse_human_duration, HumanDisplay}; use std::path::PathBuf; const GIT_VERSION: &str = git_version!(); const PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); const VERSION: &str = concatcp!(PKG_VERSION, "-", GIT_VERSION); -#[derive(Parser)] +#[derive(Parser, Clone)] #[clap(author, about, long_about = None)] #[clap(version = VERSION)] #[clap(disable_colored_help = true)] @@ -27,19 +27,19 @@ const VERSION: &str = concatcp!(PKG_VERSION, "-", GIT_VERSION); struct Cli { #[clap(subcommand)] command: Option, - //#[clap(short, long, help = "name of db file")] - //name: Option, #[clap(short, long, help = "show memos in a notification")] notify: bool, #[clap(long, help = "show completed tasks")] old: bool, #[clap(short, long, help = "synchronize memo db")] sync: bool, + #[clap(long, help = "name of db file, without extension")] + name: Option, #[clap(short, long, help = "specify location for database file")] path: Option, } -#[derive(Subcommand)] +#[derive(Subcommand, Clone)] enum Commands { /// create a new memo #[clap(trailing_var_arg = true)] @@ -69,34 +69,41 @@ enum Commands { fn main() { let args = Cli::parse(); + let args2 = args.clone(); // TODO args is already partially moved so I can't pass it all to run_commands() //might want to move the pathfinding to its own function let mut db_path: PathBuf; - let filename = "memostorage.db"; //TODO: make this configurable + let filename = args.name.unwrap_or("memostorage".to_string()); - if let Some(db) = args.path { //if we are given a starting path from command line, set it to that + if let Some(db) = args.path { // if we are given a specific path, just use that db_path = PathBuf::from(db); - } else { //else use the current path, also should not browse up if path is specified - db_path = std::env::current_dir().unwrap(); - db_path.push(filename); //check if file exists in current folder - - while !db_path.exists() && !db_path.parent().is_none() { - db_path.pop().pop(); //browse parent folders until the given filename (or root) is found - db_path.push(filename); //we want to know about the db file, not the folder itself - } - - if (!db_path.exists()) { //fallback to default - //TODO: a less "nix-centered" default fallback, possibly configurable - //protip: cfg!(windows)/cfg!(unix)/cfg!(target_os = "macos") will give us the info we need - db_path = dirs::home_dir().unwrap(); - db_path.push(".local/share/"); //default fallback path - } + } else if let Some(path) = find_db_file(filename.as_str(), SUPPORTED_FORMATS) { // search up from cwd + db_path = path; + } else { // default path + //TODO: a less "nix-centered" default fallback, possibly configurable + //protip: cfg!(windows)/cfg!(unix)/cfg!(target_os = "macos") will give us the info we need + db_path = dirs::home_dir().unwrap(); + db_path.push(".local/share/"); //default fallback path + db_path.push(format!("{}.{}", filename, "db")); // TODO hardcoded default sqlite } //TODO: permissions check, this will panic if it finds an existing db it can't write to - let mut storage = SQLiteStorage::new(db_path.to_str().unwrap(), true).unwrap(); + if let Some(ext) = db_path.extension() { + match ext.to_str().unwrap() { + "json" => run_commands(JsonStorage::new(db_path).unwrap(), args2).unwrap(), + "db" => run_commands(SQLiteStorage::new(db_path, true).unwrap(), args2).unwrap(), + _ => println!("[!] unsupported database"), + } + } else { + println!("[!] no extension on db file"); + } +} + +fn run_commands(mut storage: T, args: Cli) -> Result<(), MemoError> + where T : MemoStorage + AuthStorage + StateStorage + RemoteSync +{ if args.sync { if storage.get_key().is_err() { let key = rpassword::prompt_password("[$] new passphrase: ").unwrap(); @@ -116,7 +123,7 @@ fn main() { } } - match args.command { + match &args.command { Some(Commands::New { body, due }) => { let mut due_date: Option> = None; if let Some(d) = due { @@ -135,7 +142,7 @@ fn main() { if let Some(re) = rex.ok() { for memo in storage.all(false).unwrap() { if re.is_match(memo.body.as_str()) { - if many { + if *many { storage.del(&memo.id).unwrap(); println!("[-] done task : {}", memo.body); } else if found { @@ -164,7 +171,7 @@ fn main() { .unwrap(); if let Some(b) = body { - m.body = b; + m.body = b.to_owned(); } if let Some(d) = due { if d == "null" || d == "none" { @@ -211,7 +218,8 @@ fn main() { .summary(format!("memo-cli | {}", timing).as_str()) .body(builder.as_str()) //.icon("") //soon... - .show(); + .show() + .unwrap(); } else { println!("{} | {}", "memo-cli".bold(), timing); print!("{}", builder); @@ -239,4 +247,6 @@ fn main() { ); } } + + Ok(()) } diff --git a/src/storage.rs b/src/storage.rs index bd87860..774c36c 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -5,12 +5,13 @@ pub use sqlite::SQLiteStorage; pub use json::JsonStorage; use chrono::{DateTime, Utc}; -use std::error::Error; use std::collections::LinkedList; use std::fmt; use uuid::Uuid; use serde::{Serialize, Deserialize}; +pub const SUPPORTED_FORMATS : &'static [&'static str] = &["db", "json"]; + #[derive(Clone, Serialize, Deserialize)] pub struct Memo { pub id: Uuid, diff --git a/src/utils.rs b/src/utils.rs index 17a6b9e..fcbb3a1 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -3,6 +3,7 @@ use chrono::{Duration, Utc}; use regex::{Error, Regex}; use colored::Colorize; use std::collections::LinkedList; +use std::path::PathBuf; pub trait HumanDisplay { fn human(&self) -> String; @@ -108,6 +109,25 @@ pub fn find_by_regex(re: Regex, memos: LinkedList) -> Option { return out; } +pub fn find_db_file(fname: &str, extensions: &[&str]) -> Option { + let mut cwd = std::env::current_dir().unwrap(); // TODO handle not being able to get cwd? + let home_dir = dirs::home_dir().unwrap_or(PathBuf::new()); + + loop { + for ext in extensions { + cwd.push(format!("{}.{}", fname, ext)); + if cwd.exists() { return Some(cwd); } + cwd.pop(); + } + + if cwd.parent().is_none() { break; } + if cwd == home_dir { break; } + cwd.pop(); + } + + return None; +} + // TODO is it possible to make this a method? /* pub fn vec_to_str(arr: &Vec) -> String { let mut out = String::default();