mirror of
https://git.alemi.dev/memo-cli.git
synced 2024-11-14 05:19:20 +01:00
feat: improved cli, added edit last
This commit is contained in:
parent
c5c5082972
commit
b18ccfc290
2 changed files with 98 additions and 51 deletions
128
src/main.rs
128
src/main.rs
|
@ -42,6 +42,17 @@ struct Cli {
|
|||
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)]
|
||||
enum Commands {
|
||||
/// create a new memo
|
||||
|
@ -75,7 +86,8 @@ enum Commands {
|
|||
},
|
||||
/// change existing memo
|
||||
Edit {
|
||||
search: String,
|
||||
#[clap(subcommand, help = "search method to use")]
|
||||
mode: EditMode,
|
||||
#[clap(short, long, help = "set memo tag")]
|
||||
tag: Option<String>,
|
||||
#[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
|
||||
|
||||
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"),
|
||||
let res = match ext.to_str().unwrap() {
|
||||
"json" => run_commands(JsonStorage::new(db_path).unwrap(), args2),
|
||||
"db" => run_commands(SQLiteStorage::new(db_path, true).unwrap(), args2),
|
||||
_ => { println!("[!] unsupported database"); return; },
|
||||
};
|
||||
match res {
|
||||
Ok(()) => {},
|
||||
Err(e) => {
|
||||
println!("{} error executing command : {:?}", format!("[{}]", "!".red()).bold(), e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
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 {
|
||||
Some(Commands::New { body, due, tag }) => {
|
||||
let mut due_date: Option<DateTime<Utc>> = None;
|
||||
if let Some(d) = due {
|
||||
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(" ");
|
||||
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 }) => {
|
||||
let rex = Regex::new(search.as_str());
|
||||
let mut found = false;
|
||||
let mut to_remove: Option<Memo> = None;
|
||||
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 many {
|
||||
storage.del(&memo.id).unwrap();
|
||||
storage.del(&memo.id)?;
|
||||
println!("[-] done task : {}", memo.body);
|
||||
} else if found {
|
||||
println!("[!] would remove multiple tasks");
|
||||
|
@ -183,47 +200,82 @@ where T: StorageDriver,
|
|||
}
|
||||
}
|
||||
if let Some(rm) = to_remove {
|
||||
storage.del(&rm.id).unwrap();
|
||||
storage.del(&rm.id)?;
|
||||
println!("{} done memo: {}", "[-]".bold(), rm.body);
|
||||
}
|
||||
} else {
|
||||
println!("{} invalid regex", format!("[{}]", "!".red()).bold());
|
||||
}
|
||||
}
|
||||
Some(Commands::Edit { search, tag, body, due }) => {
|
||||
let mut m = find_by_regex(
|
||||
regex::Regex::new(search.as_str()).unwrap(),
|
||||
storage.all(false).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
Some(Commands::Edit { mode, tag, body, due }) => {
|
||||
let search : Option<Memo> = match mode {
|
||||
EditMode::Find { regex } => {
|
||||
let mut matches = find_by_regex(
|
||||
regex::Regex::new(regex.as_str()).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
|
||||
}
|
||||
|
||||
if let Some(t) = tag {
|
||||
m.tag = t;
|
||||
}
|
||||
if let Some(b) = body {
|
||||
m.body = b.to_owned();
|
||||
}
|
||||
if let Some(d) = due {
|
||||
if d == "null" || d == "none" {
|
||||
m.due = None
|
||||
} else {
|
||||
m.due = Some(Utc::now() + parse_human_duration(d.as_str()).unwrap());
|
||||
}
|
||||
},
|
||||
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 {
|
||||
changed = true;
|
||||
m.tag = t;
|
||||
}
|
||||
if let Some(b) = body {
|
||||
changed = true;
|
||||
m.body = b.to_owned();
|
||||
}
|
||||
if let Some(d) = due {
|
||||
changed = true;
|
||||
if d == "null" || d == "none" {
|
||||
m.due = None
|
||||
} else {
|
||||
m.due = Some(Utc::now() + parse_human_duration(d.as_str()));
|
||||
}
|
||||
}
|
||||
|
||||
if changed {
|
||||
let m_str = m.colored();
|
||||
storage.put(m)?;
|
||||
println!(
|
||||
"{} updated memo\n{}",
|
||||
format!("[{}]", ">".green()).bold().to_string(),
|
||||
m_str,
|
||||
);
|
||||
} else {
|
||||
println!("{} no change requested", format!("[{}]", "?".magenta()).bold());
|
||||
}
|
||||
} else {
|
||||
println!("{} no memo matches criteria", format!("[{}]", "!".red()).bold());
|
||||
}
|
||||
let m_str = m.colored();
|
||||
storage.put(m).unwrap();
|
||||
println!(
|
||||
"{} updated memo\n{}",
|
||||
format!("[{}]", ">".green()).bold().to_string(),
|
||||
m_str,
|
||||
);
|
||||
}
|
||||
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);
|
||||
},
|
||||
None => {
|
||||
let all = storage.all(false).unwrap();
|
||||
let all = storage.all(false)?;
|
||||
display_memos(all, None, &ctx, false, 5);
|
||||
}
|
||||
}
|
||||
|
|
21
src/utils.rs
21
src/utils.rs
|
@ -1,6 +1,6 @@
|
|||
use crate::model::memo::Memo;
|
||||
use chrono::{Duration, Utc};
|
||||
use regex::{Error, Regex};
|
||||
use regex::Regex;
|
||||
use colored::Colorize;
|
||||
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;
|
||||
|
||||
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(cap) = captures.get(1) {
|
||||
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> {
|
||||
let mut found = false;
|
||||
let mut out: Option<Memo> = None;
|
||||
pub fn find_by_regex(re: Regex, memos: Vec<Memo>) -> Vec<Memo> {
|
||||
let mut out: Vec<Memo> = Vec::new();
|
||||
for memo in memos {
|
||||
if re.is_match(memo.body.as_str()) {
|
||||
if found {
|
||||
return None; // there's at least one duplicate
|
||||
} else {
|
||||
out = Some(memo);
|
||||
found = true;
|
||||
}
|
||||
out.push(memo);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
|
|
Loading…
Reference in a new issue