feat: improved cli, added edit last

This commit is contained in:
əlemi 2022-12-16 02:25:41 +01:00
parent c5c5082972
commit b18ccfc290
No known key found for this signature in database
GPG key ID: BBCBFE5D7244634E
2 changed files with 98 additions and 51 deletions

View file

@ -42,6 +42,17 @@ struct Cli {
path: Option<String>, path: Option<String>,
} }
#[derive(Subcommand, Clone)]
enum EditMode {
/// search memo by content
Find {
/// regex to use for filtering
regex: String,
},
/// edit last edited memo
Last,
}
#[derive(Subcommand, Clone)] #[derive(Subcommand, Clone)]
enum Commands { enum Commands {
/// create a new memo /// create a new memo
@ -75,7 +86,8 @@ enum Commands {
}, },
/// change existing memo /// change existing memo
Edit { Edit {
search: String, #[clap(subcommand, help = "search method to use")]
mode: EditMode,
#[clap(short, long, help = "set memo tag")] #[clap(short, long, help = "set memo tag")]
tag: Option<String>, tag: Option<String>,
#[clap(short, long, help = "set memo message")] #[clap(short, long, help = "set memo message")]
@ -111,14 +123,19 @@ fn main() {
//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
if let Some(ext) = db_path.extension() { if let Some(ext) = db_path.extension() {
match ext.to_str().unwrap() { let res = match ext.to_str().unwrap() {
"json" => run_commands(JsonStorage::new(db_path).unwrap(), args2).unwrap(), "json" => run_commands(JsonStorage::new(db_path).unwrap(), args2),
"db" => run_commands(SQLiteStorage::new(db_path, true).unwrap(), args2).unwrap(), "db" => run_commands(SQLiteStorage::new(db_path, true).unwrap(), args2),
_ => println!("[!] unsupported database"), _ => { println!("[!] unsupported database"); return; },
};
match res {
Ok(()) => {},
Err(e) => {
println!("{} error executing command : {:?}", format!("[{}]", "!".red()).bold(), e);
}
} }
} else { } else {
println!("[!] no extension on db file"); println!("[!] no extension on db file");
println!("something");
} }
} }
@ -144,29 +161,29 @@ where T: StorageDriver,
// } // }
// } // }
let ctx = storage.ctx().unwrap(); let ctx = storage.ctx()?;
match args.command { match args.command {
Some(Commands::New { body, due, tag }) => { Some(Commands::New { body, due, tag }) => {
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 {
if d.len() > 0 { if d.len() > 0 {
due_date = Some(Utc::now() + parse_human_duration(d.as_str()).unwrap()); due_date = Some(Utc::now() + parse_human_duration(d.as_str()));
} }
} }
let txt = body.join(" "); let txt = body.join(" ");
println!("{} new memo: {}", "[+]".bold(), &txt); println!("{} new memo: {}", "[+]".bold(), &txt);
storage.add(tag.unwrap_or("".to_string()), txt, due_date).unwrap(); storage.add(tag.unwrap_or("".to_string()), txt, due_date)?;
} }
Some(Commands::Done { search, many }) => { Some(Commands::Done { search, many }) => {
let rex = Regex::new(search.as_str()); let rex = Regex::new(search.as_str());
let mut found = false; let mut found = false;
let mut to_remove: Option<Memo> = None; let mut to_remove: Option<Memo> = None;
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)? {
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)?;
println!("[-] done task : {}", memo.body); println!("[-] done task : {}", memo.body);
} else if found { } else if found {
println!("[!] would remove multiple tasks"); println!("[!] would remove multiple tasks");
@ -183,47 +200,82 @@ where T: StorageDriver,
} }
} }
if let Some(rm) = to_remove { if let Some(rm) = to_remove {
storage.del(&rm.id).unwrap(); storage.del(&rm.id)?;
println!("{} done memo: {}", "[-]".bold(), rm.body); println!("{} done memo: {}", "[-]".bold(), rm.body);
} }
} else { } else {
println!("{} invalid regex", format!("[{}]", "!".red()).bold()); println!("{} invalid regex", format!("[{}]", "!".red()).bold());
} }
} }
Some(Commands::Edit { search, tag, body, due }) => { Some(Commands::Edit { mode, tag, body, due }) => {
let mut m = find_by_regex( let search : Option<Memo> = match mode {
regex::Regex::new(search.as_str()).unwrap(), EditMode::Find { regex } => {
storage.all(false).unwrap(), let mut matches = find_by_regex(
) regex::Regex::new(regex.as_str()).unwrap(),
.unwrap(); storage.all(false)?,
);
match matches.len() {
0 => None,
1 => Some(matches.pop().unwrap()),
_ => {
println!("{} more than one memo matches criteria", format!("[{}]", "#".bright_red()).bold());
for memo in matches {
println!("{}", memo.human());
}
None
}
}
},
EditMode::Last => {
match ctx.last_memo {
Some(id) => Some(storage.get(&id)?),
None => None,
}
}
};
if let Some(mut m) = search {
let mut changed = false;
if let Some(t) = tag { if let Some(t) = tag {
changed = true;
m.tag = t; m.tag = t;
} }
if let Some(b) = body { if let Some(b) = body {
changed = true;
m.body = b.to_owned(); m.body = b.to_owned();
} }
if let Some(d) = due { if let Some(d) = due {
changed = true;
if d == "null" || d == "none" { if d == "null" || d == "none" {
m.due = None m.due = None
} else { } else {
m.due = Some(Utc::now() + parse_human_duration(d.as_str()).unwrap()); m.due = Some(Utc::now() + parse_human_duration(d.as_str()));
} }
} }
if changed {
let m_str = m.colored(); let m_str = m.colored();
storage.put(m).unwrap(); storage.put(m)?;
println!( println!(
"{} updated memo\n{}", "{} updated memo\n{}",
format!("[{}]", ">".green()).bold().to_string(), format!("[{}]", ">".green()).bold().to_string(),
m_str, m_str,
); );
} else {
println!("{} no change requested", format!("[{}]", "?".magenta()).bold());
}
} else {
println!("{} no memo matches criteria", format!("[{}]", "!".red()).bold());
}
} }
Some(Commands::List { tag, notify, depth, old }) => { Some(Commands::List { tag, notify, depth, old }) => {
let all = storage.all(old).unwrap(); let all = storage.all(old)?;
display_memos(all, tag, &ctx, notify, depth); display_memos(all, tag, &ctx, notify, depth);
}, },
None => { None => {
let all = storage.all(false).unwrap(); let all = storage.all(false)?;
display_memos(all, None, &ctx, false, 5); display_memos(all, None, &ctx, false, 5);
} }
} }

View file

@ -1,6 +1,6 @@
use crate::model::memo::Memo; use crate::model::memo::Memo;
use chrono::{Duration, Utc}; use chrono::{Duration, Utc};
use regex::{Error, Regex}; use regex::Regex;
use colored::Colorize; use colored::Colorize;
use std::path::PathBuf; use std::path::PathBuf;
@ -77,11 +77,12 @@ impl HumanDisplay for Memo {
} }
} }
pub fn parse_human_duration(input: &str) -> Result<Duration, Error> { pub fn parse_human_duration(input: &str) -> Duration {
let mut secs: i64 = 0; let mut secs: i64 = 0;
for (token, mult) in [("d", 60 * 60 * 24), ("h", 60 * 60), ("m", 60)] { for (token, mult) in [("d", 60 * 60 * 24), ("h", 60 * 60), ("m", 60)] {
let re = Regex::new(format!("((?:-|)[0-9]+){token}", token = token).as_str())?; // TODO don't rebuild the regex every time
let re = Regex::new(format!("((?:-|)[0-9]+){token}", token = token).as_str()).unwrap();
if let Some(captures) = re.captures(input) { if let Some(captures) = re.captures(input) {
if let Some(cap) = captures.get(1) { if let Some(cap) = captures.get(1) {
secs += mult * cap.as_str().parse::<i64>().unwrap(); // safely unwrap because regex gave only digits secs += mult * cap.as_str().parse::<i64>().unwrap(); // safely unwrap because regex gave only digits
@ -89,20 +90,14 @@ pub fn parse_human_duration(input: &str) -> Result<Duration, Error> {
} }
} }
return Ok(Duration::seconds(secs)); return Duration::seconds(secs);
} }
pub fn find_by_regex(re: Regex, memos: Vec<Memo>) -> Option<Memo> { pub fn find_by_regex(re: Regex, memos: Vec<Memo>) -> Vec<Memo> {
let mut found = false; let mut out: Vec<Memo> = Vec::new();
let mut out: Option<Memo> = None;
for memo in memos { for memo in memos {
if re.is_match(memo.body.as_str()) { if re.is_match(memo.body.as_str()) {
if found { out.push(memo);
return None; // there's at least one duplicate
} else {
out = Some(memo);
found = true;
}
} }
} }
return out; return out;