mirror of
https://git.alemi.dev/memo-cli.git
synced 2024-11-24 20:34: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 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
|
||||||
|
|
||||||
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
|
//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
|
//protip: cfg!(windows)/cfg!(unix)/cfg!(target_os = "macos") will give us the info we need
|
||||||
db_path = dirs::home_dir().unwrap();
|
db_path = dirs::home_dir().unwrap();
|
||||||
db_path.push(".local/share/"); //default fallback path
|
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
|
//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(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
20
src/utils.rs
20
src/utils.rs
|
@ -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();
|
||||||
|
|
Loading…
Reference in a new issue