mirror of
https://git.alemi.dev/memo-cli.git
synced 2024-11-24 17:44:49 +01:00
improved file lookup, load by extension
This commit is contained in:
parent
6023fcb296
commit
9af29da716
3 changed files with 60 additions and 29 deletions
58
src/main.rs
58
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<Commands>,
|
||||
//#[clap(short, long, help = "name of db file")]
|
||||
//name: Option<String>,
|
||||
#[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<String>,
|
||||
#[clap(short, long, help = "specify location for database file")]
|
||||
path: Option<String>,
|
||||
}
|
||||
|
||||
#[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
|
||||
} 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<T>(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<DateTime<Utc>> = 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(())
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
20
src/utils.rs
20
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<Memo>) -> Option<Memo> {
|
|||
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?
|
||||
/* pub fn vec_to_str<T: std::fmt::Display>(arr: &Vec<T>) -> String {
|
||||
let mut out = String::default();
|
||||
|
|
Loading…
Reference in a new issue