diff --git a/Cargo.lock b/Cargo.lock index 45ee1332f..3eaef4d79 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6064,6 +6064,7 @@ dependencies = [ "log", "num_cpus", "prettytable-rs", + "serde", "serde_json", "simplelog", "smol", diff --git a/bin/tau-cli/Cargo.toml b/bin/tau-cli/Cargo.toml index 8fc2070f8..528b784bd 100644 --- a/bin/tau-cli/Cargo.toml +++ b/bin/tau-cli/Cargo.toml @@ -28,3 +28,4 @@ prettytable-rs = "0.8.0" # Encoding and parsing serde_json = "1.0.79" +serde = {version = "1.0.136", features = ["derive"]} diff --git a/bin/tau-cli/src/main.rs b/bin/tau-cli/src/main.rs index 65b349dad..5569071e6 100644 --- a/bin/tau-cli/src/main.rs +++ b/bin/tau-cli/src/main.rs @@ -11,6 +11,7 @@ use chrono::{Datelike, Local, NaiveDate, NaiveDateTime}; use clap::{CommandFactory, Parser, Subcommand}; use log::{debug, error}; use prettytable::{cell, format, row, Cell, Row, Table}; +use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use simplelog::{ColorChoice, TermLogger, TerminalMode}; use url::Url; @@ -81,6 +82,26 @@ pub enum CliTauSubCommands { }, /// List open tasks List {}, + /// Show task by ID + Show { + /// Task ID + id: u64, + }, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +struct TaskInfo { + ref_id: String, + id: u32, + title: String, + desc: String, + assign: Vec, + project: Vec, + due: String, + rank: u32, + created_at: String, + events: Vec, + comments: Vec, } /// Tau cli @@ -145,8 +166,20 @@ async fn request(r: jsonrpc::JsonRequest, url: String) -> Result { } } +// RPCAPI: // Add new task and returns `true` upon success. -// --> {"jsonrpc": "2.0", "method": "add", "params": ["title", "desc", ["assign"], ["project"], "due", "rank"], "id": 1} +// --> {"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: &str, params: Value) -> Result { let req = jsonrpc::request(json!("add"), params); @@ -193,6 +226,14 @@ async fn set_comment(url: &str, id: u64, author: &str, content: &str) -> Result< request(req, url.to_string()).await } +// Show task by id. +// --> {"jsonrpc": "2.0", "method": "show", "params": [task_id], "id": 1} +// <-- {"jsonrpc": "2.0", "result": "task", "id": 1} +async fn show(url: &str, id: u64) -> Result { + let req = jsonrpc::request(json!("show"), json!([id])); + request(req, url.to_string()).await +} + async fn start(options: CliTau) -> Result<()> { let rpc_addr = "tcp://127.0.0.1:8875"; match options.command { @@ -260,7 +301,11 @@ async fn start(options: CliTau) -> Result<()> { let rank = rank.unwrap_or(0); - add(rpc_addr, json!([title, desc, assign, project, due, rank])).await?; + add( + rpc_addr, + json!([{"title": title, "desc": desc, "assign": assign, "project": project, "due": due, "rank": rank}]), + ) + .await?; } Some(CliTauSubCommands::List {}) => { @@ -368,11 +413,45 @@ async fn start(options: CliTau) -> Result<()> { cmnt.push_str(comment["content"].as_str().unwrap()); cmnt.push('\n'); } + cmnt.pop(); println!("Comments on Task with id {}:\n{}", id, cmnt); } } + Some(CliTauSubCommands::Show { id }) => { + let rep = show(rpc_addr, id).await?; + let due = if rep["due"].is_u64() { + let timestamp = rep["due"].as_i64().unwrap(); + NaiveDateTime::from_timestamp(timestamp, 0).date().format("%A %-d %B").to_string() + } else { + "".to_string() + }; + + let created_at = if rep["created_at"].is_u64() { + let created = rep["created_at"].as_i64().unwrap(); + NaiveDateTime::from_timestamp(created, 0).date().format("%A %-d %B").to_string() + } else { + "".to_string() + }; + + let t = TaskInfo { + ref_id: serde_json::from_value(rep["ref_id"].clone())?, + id: serde_json::from_value(rep["id"].clone())?, + title: serde_json::from_value(rep["title"].clone())?, + desc: serde_json::from_value(rep["desc"].clone())?, + assign: serde_json::from_value(rep["assign"].clone())?, + project: serde_json::from_value(rep["project"].clone())?, + due, + rank: serde_json::from_value(rep["rank"].clone())?, + created_at, + events: serde_json::from_value(rep["events"].clone())?, + comments: serde_json::from_value(rep["comments"].clone())?, + }; + // let t: TaskInfo = serde_json::from_value(rep)?; + println!("TaskInfo: {}", serde_json::to_string_pretty(&t)?); + } + _ => { error!("Please run 'tau help' to see usage."); return Err(Error::MissingParams) diff --git a/bin/taud/src/jsonrpc.rs b/bin/taud/src/jsonrpc.rs index b8e0c98e4..b0d0aaf7c 100644 --- a/bin/taud/src/jsonrpc.rs +++ b/bin/taud/src/jsonrpc.rs @@ -54,6 +54,7 @@ impl RequestHandler for JsonRpcInterface { Some("set_comment") => { return from_taud_result(self.set_comment(req.params).await, req.id) } + Some("show") => return from_taud_result(self.show(req.params).await, req.id), Some(_) | None => { return JsonResult::Err(jsonerr(ErrorCode::MethodNotFound, None, req.id)) } @@ -72,7 +73,7 @@ impl JsonRpcInterface { // "params": // [{ // "title": "..", - // "desc": ".." + // "desc": "..", // assign: [..], // project: [..], // "due": .., @@ -182,6 +183,22 @@ impl JsonRpcInterface { Ok(json!(true)) } + // RPCAPI: + // Show a task by id. + // --> {"jsonrpc": "2.0", "method": "show", "params": [task_id], "id": 1} + // <-- {"jsonrpc": "2.0", "result": "task", "id": 1} + async fn show(&self, params: Value) -> TaudResult { + let args = params.as_array().unwrap(); + + if args.len() != 1 { + return Err(TaudError::InvalidData("len of params should be 1".into())) + } + + let task: TaskInfo = self.load_task_by_id(&args[0])?; + + Ok(json!(task)) + } + fn load_task_by_id(&self, task_id: &Value) -> TaudResult { let task_id: u64 = serde_json::from_value(task_id.clone())?; diff --git a/bin/taud/src/task_info.rs b/bin/taud/src/task_info.rs index 723990c0d..a8ca6dce9 100644 --- a/bin/taud/src/task_info.rs +++ b/bin/taud/src/task_info.rs @@ -38,13 +38,13 @@ impl Comment { } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -struct TaskEvents(Vec); +pub struct TaskEvents(Vec); #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -struct TaskComments(Vec); +pub struct TaskComments(Vec); #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -struct TaskProjects(Vec); +pub struct TaskProjects(Vec); #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -struct TaskAssigns(Vec); +pub struct TaskAssigns(Vec); #[derive(Clone, Debug, Serialize, Deserialize, SerialEncodable, SerialDecodable, PartialEq)] pub struct TaskInfo {