|
@@ -1,6 +1,6 @@
|
|
|
use clap::{CommandFactory, Parser};
|
|
use clap::{CommandFactory, Parser};
|
|
|
use log::error;
|
|
use log::error;
|
|
|
-use prettytable::{cell, format, row, table, Cell, Row, Table};
|
|
|
|
|
|
|
+use prettytable::{cell, format, row, table};
|
|
|
use serde_json::{json, Value};
|
|
use serde_json::{json, Value};
|
|
|
use simplelog::{ColorChoice, TermLogger, TerminalMode};
|
|
use simplelog::{ColorChoice, TermLogger, TerminalMode};
|
|
|
|
|
|
|
@@ -18,181 +18,140 @@ mod util;
|
|
|
use crate::{
|
|
use crate::{
|
|
|
jsonrpc::{add, get_by_id, get_state, list, set_comment, set_state, update},
|
|
jsonrpc::{add, get_by_id, get_state, list, set_comment, set_state, update},
|
|
|
util::{
|
|
util::{
|
|
|
- desc_in_editor, due_as_timestamp, get_comments, get_events, get_from_task, set_title,
|
|
|
|
|
- timestamp_to_date, CliTau, CliTauSubCommands, TaskInfo, TauConfig, CONFIG_FILE_CONTENTS,
|
|
|
|
|
|
|
+ desc_in_editor, due_as_timestamp, get_comments, get_events, get_from_task, list_tasks,
|
|
|
|
|
+ set_title, timestamp_to_date, CliTau, CliTauSubCommands, TaskInfo, TauConfig,
|
|
|
|
|
+ CONFIG_FILE_CONTENTS,
|
|
|
},
|
|
},
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
async fn start(options: CliTau, config: TauConfig) -> Result<()> {
|
|
async fn start(options: CliTau, config: TauConfig) -> Result<()> {
|
|
|
- let rpc_addr = &format!("tcp://{}", &config.rpc_listen.url.clone());
|
|
|
|
|
-
|
|
|
|
|
- match options.command {
|
|
|
|
|
- Some(CliTauSubCommands::Add { title, desc, assign, project, due, rank }) => {
|
|
|
|
|
- let title = match title {
|
|
|
|
|
- Some(t) => t,
|
|
|
|
|
- None => set_title()?,
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- let desc = match desc {
|
|
|
|
|
- Some(d) => Some(d),
|
|
|
|
|
- None => desc_in_editor()?,
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- let assign: Vec<String> = match assign {
|
|
|
|
|
- Some(a) => a.split(',').map(|s| s.into()).collect(),
|
|
|
|
|
- None => vec![],
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- let project: Vec<String> = match project {
|
|
|
|
|
- Some(p) => p.split(',').map(|s| s.into()).collect(),
|
|
|
|
|
- None => vec![],
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- let due = match due {
|
|
|
|
|
- Some(d) => due_as_timestamp(&d),
|
|
|
|
|
- None => None,
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- let rank = rank.unwrap_or(0.0);
|
|
|
|
|
-
|
|
|
|
|
- add(
|
|
|
|
|
- rpc_addr,
|
|
|
|
|
- json!([{"title": title, "desc": desc, "assign": assign, "project": project, "due": due, "rank": rank}]),
|
|
|
|
|
- )
|
|
|
|
|
- .await?;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ let rpc_addr = &format!("tcp://{}", &config.rpc_listen.clone());
|
|
|
|
|
+
|
|
|
|
|
+ if !options.filter.is_empty() {
|
|
|
|
|
+ let rep = list(rpc_addr, json!([])).await?;
|
|
|
|
|
+ list_tasks(rep, options.filter)?;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ match options.command {
|
|
|
|
|
+ Some(CliTauSubCommands::Add { title, desc, assign, project, due, rank }) => {
|
|
|
|
|
+ let title = match title {
|
|
|
|
|
+ Some(t) => t,
|
|
|
|
|
+ None => set_title()?,
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- Some(CliTauSubCommands::Update { id, key, value }) => {
|
|
|
|
|
- let value = value.as_str().trim();
|
|
|
|
|
|
|
+ let desc = match desc {
|
|
|
|
|
+ Some(d) => Some(d),
|
|
|
|
|
+ None => desc_in_editor()?,
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- let updated_value: Value = match key.as_str() {
|
|
|
|
|
- "due" => {
|
|
|
|
|
- json!(due_as_timestamp(value))
|
|
|
|
|
- }
|
|
|
|
|
- "rank" => {
|
|
|
|
|
- json!(value.parse::<f32>()?)
|
|
|
|
|
- }
|
|
|
|
|
- "project" | "assign" => {
|
|
|
|
|
- json!(value.split(',').collect::<Vec<&str>>())
|
|
|
|
|
- }
|
|
|
|
|
- _ => {
|
|
|
|
|
- json!(value)
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ let assign: Vec<String> = match assign {
|
|
|
|
|
+ Some(a) => a.split(',').map(|s| s.into()).collect(),
|
|
|
|
|
+ None => vec![],
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- update(rpc_addr, id, json!({ key: updated_value })).await?;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ let project: Vec<String> = match project {
|
|
|
|
|
+ Some(p) => p.split(',').map(|s| s.into()).collect(),
|
|
|
|
|
+ None => vec![],
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ let due = match due {
|
|
|
|
|
+ Some(d) => due_as_timestamp(&d),
|
|
|
|
|
+ None => None,
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- Some(CliTauSubCommands::SetState { id, state }) => {
|
|
|
|
|
- if state.as_str() == "open" {
|
|
|
|
|
- set_state(rpc_addr, id, state.trim()).await?;
|
|
|
|
|
- } else if state.as_str() == "pause" {
|
|
|
|
|
- set_state(rpc_addr, id, state.trim()).await?;
|
|
|
|
|
- } else if state.as_str() == "stop" {
|
|
|
|
|
- set_state(rpc_addr, id, state.trim()).await?;
|
|
|
|
|
- } else {
|
|
|
|
|
- error!("Task state could only be one of three states: open, pause or stop");
|
|
|
|
|
|
|
+ let rank = rank.unwrap_or(0.0);
|
|
|
|
|
+
|
|
|
|
|
+ add(
|
|
|
|
|
+ rpc_addr,
|
|
|
|
|
+ json!([{"title": title, "desc": desc, "assign": assign, "project": project, "due": due, "rank": rank}]),
|
|
|
|
|
+ )
|
|
|
|
|
+ .await?;
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- Some(CliTauSubCommands::GetState { id }) => {
|
|
|
|
|
- let state = get_state(rpc_addr, id).await?;
|
|
|
|
|
- println!("Task with id {} is: {}", id, state);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ Some(CliTauSubCommands::Update { id, key, value }) => {
|
|
|
|
|
+ let value = value.as_str().trim();
|
|
|
|
|
+
|
|
|
|
|
+ let updated_value: Value = match key.as_str() {
|
|
|
|
|
+ "due" => {
|
|
|
|
|
+ json!(due_as_timestamp(value))
|
|
|
|
|
+ }
|
|
|
|
|
+ "rank" => {
|
|
|
|
|
+ json!(value.parse::<f32>()?)
|
|
|
|
|
+ }
|
|
|
|
|
+ "project" | "assign" => {
|
|
|
|
|
+ json!(value.split(',').collect::<Vec<&str>>())
|
|
|
|
|
+ }
|
|
|
|
|
+ _ => {
|
|
|
|
|
+ json!(value)
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- Some(CliTauSubCommands::SetComment { id, author, content }) => {
|
|
|
|
|
- set_comment(rpc_addr, id, author.trim(), content.trim()).await?;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ update(rpc_addr, id, json!({ key: updated_value })).await?;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- Some(CliTauSubCommands::GetComment { id }) => {
|
|
|
|
|
- let rep = get_by_id(rpc_addr, id).await?;
|
|
|
|
|
- let comments = get_comments(rep)?;
|
|
|
|
|
|
|
+ Some(CliTauSubCommands::SetState { id, state }) => {
|
|
|
|
|
+ if state.as_str() == "open" {
|
|
|
|
|
+ set_state(rpc_addr, id, state.trim()).await?;
|
|
|
|
|
+ } else if state.as_str() == "pause" {
|
|
|
|
|
+ set_state(rpc_addr, id, state.trim()).await?;
|
|
|
|
|
+ } else if state.as_str() == "stop" {
|
|
|
|
|
+ set_state(rpc_addr, id, state.trim()).await?;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ error!("Task state could only be one of three states: open, pause or stop");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- println!("Comments on Task with id {}:\n{}", id, comments);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ Some(CliTauSubCommands::GetState { id }) => {
|
|
|
|
|
+ let state = get_state(rpc_addr, id).await?;
|
|
|
|
|
+ println!("Task with id {} is: {}", id, state);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- Some(CliTauSubCommands::Get { id }) => {
|
|
|
|
|
- let task = get_by_id(rpc_addr, id).await?;
|
|
|
|
|
|
|
+ Some(CliTauSubCommands::SetComment { id, author, content }) => {
|
|
|
|
|
+ set_comment(rpc_addr, id, author.trim(), content.trim()).await?;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- let taskinfo: TaskInfo = serde_json::from_value(task.clone())?;
|
|
|
|
|
- let current_state: String = serde_json::from_value(get_state(rpc_addr, id).await?)?;
|
|
|
|
|
|
|
+ Some(CliTauSubCommands::GetComment { id }) => {
|
|
|
|
|
+ let rep = get_by_id(rpc_addr, id).await?;
|
|
|
|
|
+ let comments = get_comments(rep)?;
|
|
|
|
|
|
|
|
- let mut table = table!([Bd => "ref_id", &taskinfo.ref_id],
|
|
|
|
|
- ["id", &taskinfo.id.to_string()],
|
|
|
|
|
- [Bd =>"title", &taskinfo.title],
|
|
|
|
|
- ["desc", &taskinfo.desc],
|
|
|
|
|
- [Bd =>"assign", get_from_task(task.clone(), "assign")?],
|
|
|
|
|
- ["project", get_from_task(task.clone(), "project")?],
|
|
|
|
|
- [Bd =>"due", timestamp_to_date(task["due"].clone(),"date")],
|
|
|
|
|
- ["rank", &taskinfo.rank.to_string()],
|
|
|
|
|
- [Bd =>"created_at", timestamp_to_date(task["created_at"].clone(), "datetime")],
|
|
|
|
|
- ["current_state", ¤t_state],
|
|
|
|
|
- [Bd => "comments", get_comments(task.clone())?]);
|
|
|
|
|
|
|
+ println!("Comments on Task with id {}:\n{}", id, comments);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
|
|
|
|
|
- table.set_titles(row!["Name", "Value"]);
|
|
|
|
|
|
|
+ Some(CliTauSubCommands::Get { id }) => {
|
|
|
|
|
+ let task = get_by_id(rpc_addr, id).await?;
|
|
|
|
|
|
|
|
- table.printstd();
|
|
|
|
|
|
|
+ let taskinfo: TaskInfo = serde_json::from_value(task.clone())?;
|
|
|
|
|
+ let current_state: String = serde_json::from_value(get_state(rpc_addr, id).await?)?;
|
|
|
|
|
|
|
|
- let mut event_table = table!(["events", get_events(task.clone())?]);
|
|
|
|
|
- event_table.set_format(*format::consts::FORMAT_NO_COLSEP);
|
|
|
|
|
|
|
+ let mut table = table!([Bd => "ref_id", &taskinfo.ref_id],
|
|
|
|
|
+ ["id", &taskinfo.id.to_string()],
|
|
|
|
|
+ [Bd =>"title", &taskinfo.title],
|
|
|
|
|
+ ["desc", &taskinfo.desc],
|
|
|
|
|
+ [Bd =>"assign", get_from_task(task.clone(), "assign")?],
|
|
|
|
|
+ ["project", get_from_task(task.clone(), "project")?],
|
|
|
|
|
+ [Bd =>"due", timestamp_to_date(task["due"].clone(),"date")],
|
|
|
|
|
+ ["rank", &taskinfo.rank.to_string()],
|
|
|
|
|
+ [Bd =>"created_at", timestamp_to_date(task["created_at"].clone(), "datetime")],
|
|
|
|
|
+ ["current_state", ¤t_state],
|
|
|
|
|
+ [Bd => "comments", get_comments(task.clone())?]);
|
|
|
|
|
|
|
|
- event_table.printstd();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
|
|
|
|
|
+ table.set_titles(row!["Name", "Value"]);
|
|
|
|
|
|
|
|
- Some(CliTauSubCommands::List {}) | None => {
|
|
|
|
|
- let rep = list(rpc_addr, json!([])).await?;
|
|
|
|
|
-
|
|
|
|
|
- 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 mut tasks: Vec<Value> = serde_json::from_value(rep)?;
|
|
|
|
|
- tasks.sort_by(|a, b| b["rank"].as_f64().partial_cmp(&a["rank"].as_f64()).unwrap());
|
|
|
|
|
-
|
|
|
|
|
- let (max_rank, min_rank) = if !tasks.is_empty() {
|
|
|
|
|
- (
|
|
|
|
|
- serde_json::from_value(tasks[0]["rank"].clone())?,
|
|
|
|
|
- serde_json::from_value(tasks[tasks.len() - 1]["rank"].clone())?,
|
|
|
|
|
- )
|
|
|
|
|
- } else {
|
|
|
|
|
- (0.0, 0.0)
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- for task in tasks {
|
|
|
|
|
- let events: Vec<Value> = serde_json::from_value(task["events"].clone())?;
|
|
|
|
|
- let state = match events.last() {
|
|
|
|
|
- Some(s) => s["action"].as_str().unwrap(),
|
|
|
|
|
- None => "open",
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ table.printstd();
|
|
|
|
|
|
|
|
- let rank = task["rank"].as_f64().unwrap_or(0.0) as f32;
|
|
|
|
|
|
|
+ let mut event_table = table!(["events", get_events(task.clone())?]);
|
|
|
|
|
+ event_table.set_format(*format::consts::FORMAT_NO_COLSEP);
|
|
|
|
|
|
|
|
- let (max_style, min_style, mid_style, gen_style) = if state == "open" {
|
|
|
|
|
- ("bFC", "Fb", "Fc", "")
|
|
|
|
|
- } else {
|
|
|
|
|
- ("iFYBd", "iFYBd", "iFYBd", "iFYBd")
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ event_table.printstd();
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- table.add_row(Row::new(vec![
|
|
|
|
|
- Cell::new(&task["id"].to_string()).style_spec(gen_style),
|
|
|
|
|
- Cell::new(task["title"].as_str().unwrap()).style_spec(gen_style),
|
|
|
|
|
- Cell::new(&get_from_task(task.clone(), "project")?).style_spec(gen_style),
|
|
|
|
|
- Cell::new(&get_from_task(task.clone(), "assign")?).style_spec(gen_style),
|
|
|
|
|
- Cell::new(×tamp_to_date(task["due"].clone(), "date"))
|
|
|
|
|
- .style_spec(gen_style),
|
|
|
|
|
- if rank == max_rank {
|
|
|
|
|
- Cell::new(&rank.to_string()).style_spec(max_style)
|
|
|
|
|
- } else if rank == min_rank {
|
|
|
|
|
- Cell::new(&rank.to_string()).style_spec(min_style)
|
|
|
|
|
- } else {
|
|
|
|
|
- Cell::new(&rank.to_string()).style_spec(mid_style)
|
|
|
|
|
- },
|
|
|
|
|
- ]));
|
|
|
|
|
|
|
+ Some(CliTauSubCommands::List {}) | None => {
|
|
|
|
|
+ let rep = list(rpc_addr, json!([])).await?;
|
|
|
|
|
+ list_tasks(rep, vec![])?;
|
|
|
}
|
|
}
|
|
|
- table.printstd();
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
Ok(())
|
|
Ok(())
|
|
|
}
|
|
}
|
|
|
|
|
|