add basic command to edit memos

This commit is contained in:
əlemi 2022-03-16 02:07:02 +01:00
parent 581aca2fb1
commit 5d4c206b25
No known key found for this signature in database
GPG key ID: BBCBFE5D7244634E
3 changed files with 68 additions and 9 deletions

View file

@ -1,13 +1,14 @@
mod storage; mod storage;
mod utils; mod utils;
use chrono::{DateTime, Utc, Local}; use chrono::{DateTime, Local, Utc};
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use colored::Colorize;
use const_format::concatcp;
use git_version::git_version;
use regex::Regex; use regex::Regex;
use storage::{open_sqlite_storage, Memo, MemoStorage}; use storage::{open_sqlite_storage, Memo, MemoStorage};
use utils::{parse_human_duration, HumanDisplay}; use utils::{find_by_regex, parse_human_duration, HumanDisplay};
use git_version::git_version;
use const_format::concatcp;
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");
@ -47,7 +48,15 @@ enum Commands {
search: String, search: String,
#[clap(long, help = "delete more than one task if matched")] #[clap(long, help = "delete more than one task if matched")]
many: bool, many: bool,
} },
/// change existing memo
Edit {
search: String,
#[clap(short, long, help = "set memo message")]
body: Option<String>,
#[clap(short, long, help = "set due time relative to now")]
due: Option<String>, // TODO allow to pass date
},
} }
fn main() { fn main() {
@ -71,7 +80,7 @@ fn main() {
} }
let txt = body.join(" "); let txt = body.join(" ");
storage.add(txt.as_str(), due_date).unwrap(); storage.add(txt.as_str(), due_date).unwrap();
println!("[+] new memo: {}", txt); println!("{} new memo: {}", "[+]".bold(), txt);
} }
Some(Commands::Done { search, many }) => { Some(Commands::Done { search, many }) => {
let rex = Regex::new(search.as_str()); let rex = Regex::new(search.as_str());
@ -101,6 +110,26 @@ fn main() {
println!("[!] invalid regex"); 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 => { None => {
let all = storage.all(args.old).unwrap(); let all = storage.all(args.old).unwrap();
let mut builder = String::new(); let mut builder = String::new();
@ -119,11 +148,12 @@ fn main() {
let n = libnotify::Notification::new( let n = libnotify::Notification::new(
format!("memo-cli | {}", Local::now().format("%a %d/%m, %H:%M")).as_str(), format!("memo-cli | {}", Local::now().format("%a %d/%m, %H:%M")).as_str(),
Some(builder.as_str()), Some(builder.as_str()),
None None,
); );
n.show().unwrap(); n.show().unwrap();
libnotify::uninit(); libnotify::uninit();
} else { } else {
println!("{}", "memo-cli".bold());
print!("{}", builder); print!("{}", builder);
} }
} }

View file

@ -27,6 +27,7 @@ impl fmt::Display for Memo {
pub trait MemoStorage { pub trait MemoStorage {
fn all(&self, done: bool) -> Result<Vec<Memo>, Error>; fn all(&self, done: bool) -> Result<Vec<Memo>, Error>;
fn add(&self, body: &str, due: Option<DateTime<Utc>>) -> Result<(), Error>; fn add(&self, body: &str, due: Option<DateTime<Utc>>) -> Result<(), Error>;
fn set(&self, memo: &Memo) -> Result<bool, Error>;
fn del(&self, id: u32) -> Result<bool, Error>; fn del(&self, id: u32) -> Result<bool, Error>;
fn get(&self, id: u32) -> Result<Memo, Error>; fn get(&self, id: u32) -> Result<Memo, Error>;
} }
@ -109,6 +110,18 @@ impl MemoStorage for SQLiteStorage {
return Ok(()); return Ok(());
} }
fn set(&self, memo: &Memo) -> Result<bool, Error> {
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<bool, Error> { fn del(&self, id: u32) -> Result<bool, Error> {
let count = self let count = self
.conn .conn

View file

@ -63,8 +63,24 @@ pub fn parse_human_duration(input: &str) -> Result<Duration, Error> {
return Ok(Duration::seconds(secs)); return Ok(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;
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? // 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();
let mut first = true; let mut first = true;
out += "["; out += "[";
@ -78,4 +94,4 @@ pub fn vec_to_str<T: std::fmt::Display>(arr: &Vec<T>) -> String {
} }
out += " ]"; out += " ]";
return out; return out;
} } */