diff --git a/src/main.rs b/src/main.rs index b2f8b70..41357eb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,14 @@ mod storage; mod utils; -use chrono::{DateTime, Utc, Local}; +use chrono::{DateTime, Local, Utc}; use clap::{Parser, Subcommand}; +use colored::Colorize; +use const_format::concatcp; +use git_version::git_version; use regex::Regex; use storage::{open_sqlite_storage, Memo, MemoStorage}; -use utils::{parse_human_duration, HumanDisplay}; -use git_version::git_version; -use const_format::concatcp; +use utils::{find_by_regex, parse_human_duration, HumanDisplay}; const GIT_VERSION: &str = git_version!(); const PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -47,7 +48,15 @@ enum Commands { search: String, #[clap(long, help = "delete more than one task if matched")] many: bool, - } + }, + /// change existing memo + Edit { + search: String, + #[clap(short, long, help = "set memo message")] + body: Option, + #[clap(short, long, help = "set due time relative to now")] + due: Option, // TODO allow to pass date + }, } fn main() { @@ -71,7 +80,7 @@ fn main() { } let txt = body.join(" "); storage.add(txt.as_str(), due_date).unwrap(); - println!("[+] new memo: {}", txt); + println!("{} new memo: {}", "[+]".bold(), txt); } Some(Commands::Done { search, many }) => { let rex = Regex::new(search.as_str()); @@ -101,6 +110,26 @@ fn main() { println!("[!] invalid regex"); } } + Some(Commands::Edit { search, body, due }) => { + let mut m = find_by_regex( + regex::Regex::new(search.as_str()).unwrap(), + storage.all(false).unwrap(), + ) + .unwrap(); + + if let Some(b) = body { + m.body = b; + } + 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()); + } + } + storage.set(&m).unwrap(); + println!("[>] updated memo\n{}", m.human()); + } None => { let all = storage.all(args.old).unwrap(); let mut builder = String::new(); @@ -119,11 +148,12 @@ fn main() { let n = libnotify::Notification::new( format!("memo-cli | {}", Local::now().format("%a %d/%m, %H:%M")).as_str(), Some(builder.as_str()), - None + None, ); n.show().unwrap(); libnotify::uninit(); } else { + println!("{}", "memo-cli".bold()); print!("{}", builder); } } diff --git a/src/storage.rs b/src/storage.rs index f708559..1950570 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -27,6 +27,7 @@ impl fmt::Display for Memo { pub trait MemoStorage { fn all(&self, done: bool) -> Result, Error>; fn add(&self, body: &str, due: Option>) -> Result<(), Error>; + fn set(&self, memo: &Memo) -> Result; fn del(&self, id: u32) -> Result; fn get(&self, id: u32) -> Result; } @@ -109,6 +110,18 @@ impl MemoStorage for SQLiteStorage { return Ok(()); } + fn set(&self, memo: &Memo) -> Result { + let count = self.conn.execute( + "UPDATE memo SET body = ?, due = ? WHERE id = ?", + params![memo.body, memo.due, memo.id], + )?; + if count > 0 { + return Ok(true); + } else { + return Ok(false); + } + } + fn del(&self, id: u32) -> Result { let count = self .conn diff --git a/src/utils.rs b/src/utils.rs index 247949a..00c230f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -63,8 +63,24 @@ pub fn parse_human_duration(input: &str) -> Result { return Ok(Duration::seconds(secs)); } +pub fn find_by_regex(re: Regex, memos: Vec) -> Option { + let mut found = false; + let mut out: Option = None; + 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; + } + } + } + return out; +} + // TODO is it possible to make this a method? -pub fn vec_to_str(arr: &Vec) -> String { +/* pub fn vec_to_str(arr: &Vec) -> String { let mut out = String::default(); let mut first = true; out += "["; @@ -78,4 +94,4 @@ pub fn vec_to_str(arr: &Vec) -> String { } out += " ]"; return out; -} +} */