2022-03-14 03:41:26 +01:00
|
|
|
mod storage;
|
|
|
|
mod utils;
|
|
|
|
|
2022-03-14 23:12:41 +01:00
|
|
|
use chrono::{DateTime, Utc, Local};
|
2022-03-14 03:41:26 +01:00
|
|
|
use clap::{Parser, Subcommand};
|
|
|
|
use regex::Regex;
|
2022-03-15 22:17:24 +01:00
|
|
|
use storage::{open_sqlite_storage, Memo, MemoStorage};
|
2022-03-14 03:41:26 +01:00
|
|
|
use utils::{parse_human_duration, HumanDisplay};
|
2022-03-15 22:17:24 +01:00
|
|
|
use git_version::git_version;
|
|
|
|
use const_format::concatcp;
|
|
|
|
|
|
|
|
const GIT_VERSION: &str = git_version!();
|
|
|
|
const PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
|
|
|
|
const VERSION: &str = concatcp!(PKG_VERSION, "-", GIT_VERSION);
|
2022-03-14 03:41:26 +01:00
|
|
|
|
|
|
|
#[derive(Parser)]
|
2022-03-15 22:17:24 +01:00
|
|
|
#[clap(author, about, long_about = None)]
|
|
|
|
#[clap(version = VERSION)]
|
2022-03-14 23:12:41 +01:00
|
|
|
#[clap(disable_colored_help = true)]
|
2022-03-14 03:41:26 +01:00
|
|
|
#[clap(subcommand_required = false)]
|
|
|
|
#[clap(disable_help_subcommand = true)]
|
|
|
|
struct Cli {
|
|
|
|
#[clap(subcommand)]
|
|
|
|
command: Option<Commands>,
|
2022-03-14 23:12:41 +01:00
|
|
|
#[clap(short, long, help = "show memos in a notification")]
|
|
|
|
notify: bool,
|
2022-03-14 22:41:32 +01:00
|
|
|
#[clap(long, help = "show completed tasks")]
|
|
|
|
old: bool,
|
2022-03-14 23:12:41 +01:00
|
|
|
#[clap(short, long, help = "location for database file")]
|
2022-03-15 22:17:24 +01:00
|
|
|
path: Option<String>,
|
2022-03-13 20:13:13 +01:00
|
|
|
}
|
|
|
|
|
2022-03-14 03:41:26 +01:00
|
|
|
#[derive(Subcommand)]
|
|
|
|
enum Commands {
|
2022-03-15 22:17:24 +01:00
|
|
|
/// create a new memo
|
2022-03-14 03:41:26 +01:00
|
|
|
#[clap(trailing_var_arg = true)]
|
|
|
|
New {
|
|
|
|
#[clap(multiple_values = true)]
|
|
|
|
#[clap(min_values = 1)]
|
|
|
|
#[clap(required = true)]
|
|
|
|
body: Vec<String>,
|
|
|
|
#[clap(short, long, help = "due time relative to now")]
|
2022-03-14 23:12:41 +01:00
|
|
|
due: Option<String>, // TODO allow to pass date
|
2022-03-14 03:41:26 +01:00
|
|
|
},
|
2022-03-15 22:17:24 +01:00
|
|
|
/// mark existing memo as done
|
2022-03-14 03:41:26 +01:00
|
|
|
Done {
|
|
|
|
search: String,
|
2022-03-14 23:12:41 +01:00
|
|
|
#[clap(long, help = "delete more than one task if matched")]
|
2022-03-14 03:41:26 +01:00
|
|
|
many: bool,
|
2022-03-14 23:12:41 +01:00
|
|
|
}
|
2022-03-13 20:13:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
2022-03-14 03:41:26 +01:00
|
|
|
let args = Cli::parse();
|
2022-03-13 20:13:13 +01:00
|
|
|
let home_path = std::env!("HOME").to_string();
|
|
|
|
let mut db_path: String = home_path + "/.local/share/memo-cli.db";
|
|
|
|
|
2022-03-15 22:17:24 +01:00
|
|
|
if let Some(db) = args.path {
|
2022-03-14 03:41:26 +01:00
|
|
|
db_path = db;
|
2022-03-13 20:13:13 +01:00
|
|
|
}
|
|
|
|
|
2022-03-14 03:41:26 +01:00
|
|
|
let storage = open_sqlite_storage(&db_path).unwrap();
|
2022-03-13 20:13:13 +01:00
|
|
|
|
2022-03-14 03:41:26 +01:00
|
|
|
match args.command {
|
|
|
|
Some(Commands::New { body, due }) => {
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let txt = body.join(" ");
|
|
|
|
storage.add(txt.as_str(), due_date).unwrap();
|
2022-03-14 03:49:35 +01:00
|
|
|
println!("[+] new memo: {}", txt);
|
2022-03-14 03:41:26 +01:00
|
|
|
}
|
|
|
|
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() {
|
2022-03-14 22:41:32 +01:00
|
|
|
for memo in storage.all(false).unwrap() {
|
2022-03-14 03:41:26 +01:00
|
|
|
if re.is_match(memo.body.as_str()) {
|
|
|
|
if many {
|
|
|
|
storage.del(memo.id).unwrap();
|
|
|
|
println!("[-] task #{} done", memo.id);
|
|
|
|
} else if found {
|
|
|
|
println!("[!] would remove multiple tasks");
|
|
|
|
to_remove = None;
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
to_remove = Some(memo);
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let Some(rm) = to_remove {
|
|
|
|
storage.del(rm.id).unwrap();
|
|
|
|
println!("[-] task #{} done", rm.id);
|
|
|
|
}
|
2022-03-13 20:13:13 +01:00
|
|
|
} else {
|
2022-03-14 03:41:26 +01:00
|
|
|
println!("[!] invalid regex");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => {
|
2022-03-14 22:41:32 +01:00
|
|
|
let all = storage.all(args.old).unwrap();
|
2022-03-14 23:12:41 +01:00
|
|
|
let mut builder = String::new();
|
|
|
|
if args.old {
|
|
|
|
builder.push_str("Archived memos:\n");
|
|
|
|
}
|
2022-03-14 03:49:35 +01:00
|
|
|
if all.len() < 1 {
|
2022-03-14 23:12:41 +01:00
|
|
|
builder.push_str("[ ] nothing to remember\n");
|
2022-03-14 03:49:35 +01:00
|
|
|
}
|
|
|
|
for m in all {
|
2022-03-14 23:12:41 +01:00
|
|
|
builder.push_str(m.human().as_str());
|
|
|
|
builder.push('\n');
|
|
|
|
}
|
|
|
|
if args.notify {
|
|
|
|
libnotify::init("memo-cli").unwrap();
|
|
|
|
let n = libnotify::Notification::new(
|
|
|
|
format!("memo-cli | {}", Local::now().format("%a %d/%m, %H:%M")).as_str(),
|
|
|
|
Some(builder.as_str()),
|
|
|
|
None
|
|
|
|
);
|
|
|
|
n.show().unwrap();
|
|
|
|
libnotify::uninit();
|
|
|
|
} else {
|
|
|
|
print!("{}", builder);
|
2022-03-13 20:13:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|