From 7a4e85de96cf6e6da836adda21e2f716df3624c2 Mon Sep 17 00:00:00 2001 From: Dastan-glitch Date: Tue, 15 Mar 2022 17:50:00 -0400 Subject: [PATCH] bin/tau-cli: Add list subcommand --- Cargo.lock | 1 + bin/tau-cli/Cargo.toml | 3 +- bin/tau-cli/src/main.rs | 260 +++++++++++++++++++++++++++------------- 3 files changed, 183 insertions(+), 81 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8fe6c2b39..3e25a8746 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5288,6 +5288,7 @@ dependencies = [ "futures", "log", "num_cpus", + "prettytable-rs", "serde_json", "simplelog", "smol", diff --git a/bin/tau-cli/Cargo.toml b/bin/tau-cli/Cargo.toml index dc1514db4..75c931d0a 100644 --- a/bin/tau-cli/Cargo.toml +++ b/bin/tau-cli/Cargo.toml @@ -20,12 +20,13 @@ async-executor = "1.4.1" easy-parallel = "3.2.0" # Misc -clap = {version = "3.0.7", features = ["derive"]} +clap = {version = "3.1.6", features = ["derive"]} log = "0.4.14" num_cpus = "1.13.1" simplelog = "0.11.2" url = "2.2.2" chrono = "0.4.19" +prettytable-rs = "0.8.0" # Encoding and parsing serde_json = "1.0.74" diff --git a/bin/tau-cli/src/main.rs b/bin/tau-cli/src/main.rs index 023967b7d..a55c8c793 100644 --- a/bin/tau-cli/src/main.rs +++ b/bin/tau-cli/src/main.rs @@ -1,11 +1,14 @@ -use chrono::{Datelike, Local, NaiveDate}; -use clap::{AppSettings, IntoApp, Parser, Subcommand}; +// TODO: This whole code needs refactoring, clean-up and comments +use chrono::{Datelike, Local, NaiveDate, NaiveDateTime}; +use clap::{CommandFactory, Parser, Subcommand}; use log::{debug, error}; +use prettytable::{cell, format, row, Table}; use std::{ env::{temp_dir, var}, fs::{self, File}, io::{Read, Write}, + ops::Index, }; use darkfi::{ @@ -30,26 +33,28 @@ pub enum CliTauSubCommands { desc: Option, /// Assign task to user #[clap(short, long)] - assign: Option>, + assign: Option, /// Task project (can be hierarchical: crypto.zk) #[clap(short, long)] - project: Option>, + project: Option, /// Due date in DDMM format: "2202" for 22 Feb #[clap(short, long)] - due: Option, + due: Option, /// Project rank #[clap(short, long)] rank: Option, }, + /// List open tasks + List { + #[clap(short, long)] + month: Option, + }, } /// Tau cli #[derive(Parser)] #[clap(name = "tau")] #[clap(author, version, about)] -#[clap(global_setting(AppSettings::PropagateVersion))] -#[clap(global_setting(AppSettings::UseLongFormatForHelpSubcommand))] -#[clap(setting(AppSettings::SubcommandRequiredElseHelp))] pub struct CliTau { /// Increase verbosity #[clap(short, parse(from_occurrences))] @@ -86,87 +91,182 @@ async fn request(r: jsonrpc::JsonRequest, url: String) -> Result { // --> {"jsonrpc": "2.0", "method": "add", "params": ["title", "desc", ["assign"], ["project"], "due", "rank"], "id": 1} // <-- {"jsonrpc": "2.0", "result": true, "id": 1} async fn add( - url: String, - title: Option, - desc: Option, - assign: Option>, - project: Option>, - due: Option, + url: &str, + title: Option<&str>, + desc: Option<&str>, + assign: Option>, + project: Option>, + due: Option, rank: Option, ) -> Result { let req = jsonrpc::request(json!("add"), json!([title, desc, assign, project, due, rank])); - Ok(request(req, url).await?) + Ok(request(req, url.to_string()).await?) +} + +// List tasks +// --> {"jsonrpc": "2.0", "method": "list", "params": [month_date], "id": 1} +// <-- {"jsonrpc": "2.0", "result": [task, ...], "id": 1} +async fn list(url: &str, month: Option) -> Result { + let req = jsonrpc::request(json!("list"), json!([month])); + Ok(request(req, url.to_string()).await?) } async fn start(options: CliTau) -> Result<()> { let rpc_addr = "tcp://127.0.0.1:8875"; - if let Some(CliTauSubCommands::Add { title, desc, assign, project, due, rank }) = - options.command - { - let t = if title.is_none() { - print!("Title: "); - io::stdout().flush()?; - let mut t = String::new(); - io::stdin().read_line(&mut t)?; - if &t[(t.len() - 1)..] == "\n" { - t.pop(); - } - Some(t) - } else { - title - }; - - let des = if desc.is_none() { - let editor = var("EDITOR").unwrap(); - let mut file_path = temp_dir(); - file_path.push("temp_file"); - File::create(&file_path)?; - fs::write( - &file_path, - "\n# Write task description above this line\n# These lines will be removed\n", - )?; - - Command::new(editor).arg(&file_path).status()?; - - let mut lines = String::new(); - File::open(file_path)?.read_to_string(&mut lines)?; - - let mut description = String::new(); - for line in lines.split('\n') { - if !line.starts_with('#') { - description.push_str(line) + match options.command { + Some(CliTauSubCommands::Add { title, desc, assign, project, due, rank }) => { + let t = if title.is_none() { + print!("Title: "); + io::stdout().flush()?; + let mut t = String::new(); + io::stdin().read_line(&mut t)?; + if &t[(t.len() - 1)..] == "\n" { + t.pop(); } + Some(t) + } else { + title + }; + + let des = if desc.is_none() { + let editor = var("EDITOR").unwrap(); + let mut file_path = temp_dir(); + file_path.push("temp_file"); + File::create(&file_path)?; + fs::write( + &file_path, + "\n# Write task description above this line\n# These lines will be removed\n", + )?; + + Command::new(editor).arg(&file_path).status()?; + + let mut lines = String::new(); + File::open(file_path)?.read_to_string(&mut lines)?; + + let mut description = String::new(); + for line in lines.split('\n') { + if !line.starts_with('#') { + description.push_str(line) + } + } + + Some(description) + } else { + desc + }; + + // fix this + let assignee; + let assigned; + let a = if assign.is_some() { + assignee = assign.unwrap(); + assigned = assignee.as_str(); + let somevec = assigned.split(',').collect(); + + Some(somevec) + } else { + None + }; + + // fix this + let projecte; + let projectd; + let p = if project.is_some() { + projecte = project.unwrap(); + projectd = projecte.as_str(); + let somevec = projectd.split(',').collect(); + + Some(somevec) + } else { + None + }; + + let d = if due.is_some() { + let du = due.unwrap(); + assert!(du.len() == 4); + let (day, month) = (du[..2].parse::()?, du[2..].parse::()?); + let mut year = Local::today().year(); + if month < Local::today().month() { + year += 1; + } + if month == Local::today().month() && day < Local::today().day() { + year += 1; + } + let dt = NaiveDate::from_ymd(year, month, day).and_hms(12, 0, 0); + // let dt_string = dt.format("%A %-d %B").to_string(); // Format: Weekday Day Month + let timestamp = dt.timestamp(); + Some(timestamp) + } else { + None + }; + + let r = if rank.is_none() { Some(0) } else { rank }; + + add(rpc_addr, t.as_deref(), des.as_deref(), a, p, d, r).await?; + //println!("Added task: {:#?}", t); + return Ok(()) + } + Some(CliTauSubCommands::List { month }) => { + let ts = if month.is_some() { + let month = month.unwrap(); + assert!(month.len() == 4); + let (m, y) = (month[..2].parse::()?, month[2..].parse::()?); + let dt = NaiveDate::from_ymd(y + 2000, m, 1).and_hms(0, 0, 0); + + Some(dt.timestamp()) + } else { + None + }; + + let mut table = Table::new(); + table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR); + table.set_titles(row!["ID", "Title", "Project", "Assigned", "Due", "Rank"]); + + let rep = list(rpc_addr, ts).await?; + + let tasks = rep.as_array().unwrap(); + for task in tasks { + let project = task["project"].as_array().unwrap(); + let mut projects = String::new(); + for (i, _) in project.iter().enumerate() { + if !projects.is_empty() { + projects.push(','); + } + projects.push_str(project.index(i).as_str().unwrap()); + } + + let assign = task["assign"].as_array().unwrap(); + let mut asgn = String::new(); + for (i, _) in assign.iter().enumerate() { + if !asgn.is_empty() { + asgn.push(','); + } + asgn.push_str(assign.index(i).as_str().unwrap()); + } + + let date = if task["due"].is_u64() { + let due = task["due"].as_i64().unwrap(); + NaiveDateTime::from_timestamp(due, 0).date().format("%A %-d %B").to_string() + } else { + "".to_string() + }; + + // TODO: sort lines in table by rank + // TODO: the higher the rank the brighter it is + table.add_row(row![ + task["id"], + task["title"].as_str().unwrap(), + projects, + asgn, + date, + Fb->task["rank"] + ]); } + table.printstd(); - Some(description) - } else { - desc - }; - - let d = if due.is_some() { - let du = due.unwrap().to_string(); - assert!(du.len() == 4); - let (day, month) = (du[..2].parse::()?, du[2..].parse::()?); - let mut year = Local::today().year(); - if month < Local::today().month() { - year += 1; - } - if month == Local::today().month() && day < Local::today().day() { - year += 1; - } - let dt = NaiveDate::from_ymd(year, month, day).and_hms(12, 0, 0); - // let dt_string = dt.format("%A %-d %B").to_string(); // Format: Weekday Day Month - let timestamp = dt.timestamp().try_into().unwrap(); - Some(timestamp) - } else { - None - }; - - let r = if rank.is_none() { Some(0) } else { rank }; - - add(rpc_addr.to_string(), t, des, assign, project, d, r).await?; - //println!("Added task: {:#?}", t); - return Ok(()) + return Ok(()) + } + _ => (), } error!("Please run 'tau help' to see usage."); @@ -176,7 +276,7 @@ async fn start(options: CliTau) -> Result<()> { #[async_std::main] async fn main() -> Result<()> { let args = CliTau::parse(); - let matches = CliTau::into_app().get_matches(); + let matches = CliTau::command().get_matches(); let verbosity_level = matches.occurrences_of("verbose"); //let config_path = if args.config.is_some() {