improved file lookup, load by extension

This commit is contained in:
əlemi 2022-03-28 01:52:05 +02:00
parent 6023fcb296
commit 9af29da716
No known key found for this signature in database
GPG key ID: BBCBFE5D7244634E
3 changed files with 60 additions and 29 deletions

View file

@ -10,15 +10,15 @@ use git_version::git_version;
use notify_rust::Notification; use notify_rust::Notification;
use regex::Regex; use regex::Regex;
use remote::RemoteSync; use remote::RemoteSync;
use storage::{AuthStorage, Memo, MemoStorage, SQLiteStorage, StateStorage, JsonStorage}; use storage::{AuthStorage, Memo, MemoError, MemoStorage, SQLiteStorage, StateStorage, JsonStorage, SUPPORTED_FORMATS};
use utils::{find_by_regex, parse_human_duration, HumanDisplay}; use utils::{find_by_regex, find_db_file, parse_human_duration, HumanDisplay};
use std::path::PathBuf; use std::path::PathBuf;
const GIT_VERSION: &str = git_version!(); const GIT_VERSION: &str = git_version!();
const PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); const PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
const VERSION: &str = concatcp!(PKG_VERSION, "-", GIT_VERSION); const VERSION: &str = concatcp!(PKG_VERSION, "-", GIT_VERSION);
#[derive(Parser)] #[derive(Parser, Clone)]
#[clap(author, about, long_about = None)] #[clap(author, about, long_about = None)]
#[clap(version = VERSION)] #[clap(version = VERSION)]
#[clap(disable_colored_help = true)] #[clap(disable_colored_help = true)]
@ -27,19 +27,19 @@ const VERSION: &str = concatcp!(PKG_VERSION, "-", GIT_VERSION);
struct Cli { struct Cli {
#[clap(subcommand)] #[clap(subcommand)]
command: Option<Commands>, command: Option<Commands>,
//#[clap(short, long, help = "name of db file")]
//name: Option<String>,
#[clap(short, long, help = "show memos in a notification")] #[clap(short, long, help = "show memos in a notification")]
notify: bool, notify: bool,
#[clap(long, help = "show completed tasks")] #[clap(long, help = "show completed tasks")]
old: bool, old: bool,
#[clap(short, long, help = "synchronize memo db")] #[clap(short, long, help = "synchronize memo db")]
sync: bool, sync: bool,
#[clap(long, help = "name of db file, without extension")]
name: Option<String>,
#[clap(short, long, help = "specify location for database file")] #[clap(short, long, help = "specify location for database file")]
path: Option<String>, path: Option<String>,
} }
#[derive(Subcommand)] #[derive(Subcommand, Clone)]
enum Commands { enum Commands {
/// create a new memo /// create a new memo
#[clap(trailing_var_arg = true)] #[clap(trailing_var_arg = true)]
@ -69,34 +69,41 @@ enum Commands {
fn main() { fn main() {
let args = Cli::parse(); 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 //might want to move the pathfinding to its own function
let mut db_path: PathBuf; 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); db_path = PathBuf::from(db);
} else { //else use the current path, also should not browse up if path is specified } else if let Some(path) = find_db_file(filename.as_str(), SUPPORTED_FORMATS) { // search up from cwd
db_path = std::env::current_dir().unwrap(); db_path = path;
db_path.push(filename); //check if file exists in current folder } else { // default path
//TODO: a less "nix-centered" default fallback, possibly configurable
while !db_path.exists() && !db_path.parent().is_none() { //protip: cfg!(windows)/cfg!(unix)/cfg!(target_os = "macos") will give us the info we need
db_path.pop().pop(); //browse parent folders until the given filename (or root) is found db_path = dirs::home_dir().unwrap();
db_path.push(filename); //we want to know about the db file, not the folder itself db_path.push(".local/share/"); //default fallback path
} db_path.push(format!("{}.{}", filename, "db")); // TODO hardcoded default sqlite
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
}
} }
//TODO: permissions check, this will panic if it finds an existing db it can't write to //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<T>(mut storage: T, args: Cli) -> Result<(), MemoError>
where T : MemoStorage + AuthStorage + StateStorage + RemoteSync
{
if args.sync { if args.sync {
if storage.get_key().is_err() { if storage.get_key().is_err() {
let key = rpassword::prompt_password("[$] new passphrase: ").unwrap(); 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 }) => { Some(Commands::New { body, due }) => {
let mut due_date: Option<DateTime<Utc>> = None; let mut due_date: Option<DateTime<Utc>> = None;
if let Some(d) = due { if let Some(d) = due {
@ -135,7 +142,7 @@ fn main() {
if let Some(re) = rex.ok() { if let Some(re) = rex.ok() {
for memo in storage.all(false).unwrap() { for memo in storage.all(false).unwrap() {
if re.is_match(memo.body.as_str()) { if re.is_match(memo.body.as_str()) {
if many { if *many {
storage.del(&memo.id).unwrap(); storage.del(&memo.id).unwrap();
println!("[-] done task : {}", memo.body); println!("[-] done task : {}", memo.body);
} else if found { } else if found {
@ -164,7 +171,7 @@ fn main() {
.unwrap(); .unwrap();
if let Some(b) = body { if let Some(b) = body {
m.body = b; m.body = b.to_owned();
} }
if let Some(d) = due { if let Some(d) = due {
if d == "null" || d == "none" { if d == "null" || d == "none" {
@ -211,7 +218,8 @@ fn main() {
.summary(format!("memo-cli | {}", timing).as_str()) .summary(format!("memo-cli | {}", timing).as_str())
.body(builder.as_str()) .body(builder.as_str())
//.icon("") //soon... //.icon("") //soon...
.show(); .show()
.unwrap();
} else { } else {
println!("{} | {}", "memo-cli".bold(), timing); println!("{} | {}", "memo-cli".bold(), timing);
print!("{}", builder); print!("{}", builder);
@ -239,4 +247,6 @@ fn main() {
); );
} }
} }
Ok(())
} }

View file

@ -5,12 +5,13 @@ pub use sqlite::SQLiteStorage;
pub use json::JsonStorage; pub use json::JsonStorage;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use std::error::Error;
use std::collections::LinkedList; use std::collections::LinkedList;
use std::fmt; use std::fmt;
use uuid::Uuid; use uuid::Uuid;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
pub const SUPPORTED_FORMATS : &'static [&'static str] = &["db", "json"];
#[derive(Clone, Serialize, Deserialize)] #[derive(Clone, Serialize, Deserialize)]
pub struct Memo { pub struct Memo {
pub id: Uuid, pub id: Uuid,

View file

@ -3,6 +3,7 @@ use chrono::{Duration, Utc};
use regex::{Error, Regex}; use regex::{Error, Regex};
use colored::Colorize; use colored::Colorize;
use std::collections::LinkedList; use std::collections::LinkedList;
use std::path::PathBuf;
pub trait HumanDisplay { pub trait HumanDisplay {
fn human(&self) -> String; fn human(&self) -> String;
@ -108,6 +109,25 @@ pub fn find_by_regex(re: Regex, memos: LinkedList<Memo>) -> Option<Memo> {
return out; return out;
} }
pub fn find_db_file(fname: &str, extensions: &[&str]) -> Option<PathBuf> {
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? // TODO is it possible to make this a method?
/* pub fn vec_to_str<T: std::fmt::Display>(arr: &Vec<T>) -> String { /* pub fn vec_to_str<T: std::fmt::Display>(arr: &Vec<T>) -> String {
let mut out = String::default(); let mut out = String::default();