Răsfoiți Sursa

bin/tau-cli: Add list subcommand

Dastan-glitch 4 ani în urmă
părinte
comite
7a4e85de96
3 a modificat fișierele cu 183 adăugiri și 81 ștergeri
  1. 1 0
      Cargo.lock
  2. 2 1
      bin/tau-cli/Cargo.toml
  3. 180 80
      bin/tau-cli/src/main.rs

+ 1 - 0
Cargo.lock

@@ -5288,6 +5288,7 @@ dependencies = [
  "futures",
  "log",
  "num_cpus",
+ "prettytable-rs",
  "serde_json",
  "simplelog",
  "smol",

+ 2 - 1
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"

+ 180 - 80
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<String>,
         /// Assign task to user
         #[clap(short, long)]
-        assign: Option<Vec<String>>,
+        assign: Option<String>,
         /// Task project (can be hierarchical: crypto.zk)
         #[clap(short, long)]
-        project: Option<Vec<String>>,
+        project: Option<String>,
         /// Due date in DDMM format: "2202" for 22 Feb
         #[clap(short, long)]
-        due: Option<u64>,
+        due: Option<String>,
         /// Project rank
         #[clap(short, long)]
         rank: Option<u32>,
     },
+    /// List open tasks
+    List {
+        #[clap(short, long)]
+        month: Option<String>,
+    },
 }
 
 /// 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<Value> {
 // --> {"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<String>,
-    desc: Option<String>,
-    assign: Option<Vec<String>>,
-    project: Option<Vec<String>>,
-    due: Option<u64>,
+    url: &str,
+    title: Option<&str>,
+    desc: Option<&str>,
+    assign: Option<Vec<&str>>,
+    project: Option<Vec<&str>>,
+    due: Option<i64>,
     rank: Option<u32>,
 ) -> Result<Value> {
     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<i64>) -> Result<Value> {
+    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
+            };
 
-            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::<u32>()?, du[2..].parse::<u32>()?);
-            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 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::<u32>()?, du[2..].parse::<u32>()?);
+                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::<u32>()?, month[2..].parse::<i32>()?);
+                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"]
+                ]);
             }
-            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(())
+            table.printstd();
+
+            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() {