瀏覽代碼

taud: Finish new RPC port.

parazyd 3 年之前
父節點
當前提交
2eea9daffd

+ 2 - 1
Cargo.lock

@@ -5952,10 +5952,11 @@ dependencies = [
  "log",
  "prettytable-rs",
  "serde",
- "serde_json",
  "simplelog",
+ "taud",
  "term_grid",
  "textwrap 0.16.0",
+ "tinyjson",
  "url",
 ]
 

+ 3 - 2
bin/tau/tau-cli/Cargo.toml

@@ -10,7 +10,8 @@ repository = "https://github.com/darkrenaissance/darkfi"
 
 [dependencies]
 libc = "0.2.147"
-darkfi = { path = "../../../", features = ["rpc"]}
+darkfi = {path = "../../../", features = ["rpc"]}
+taud = {path = "../taud"}
 
 # Async
 async-std = {version = "1.12.0", features = ["attributes"]}
@@ -29,4 +30,4 @@ url = "2.4.0"
 # Encoding and parsing
 clap = {version = "4.3.22", features = ["derive"]}
 serde = {version = "1.0.183", features = ["derive"]}
-serde_json = "1.0.105"
+tinyjson = "2.5.1"

+ 19 - 15
bin/tau/tau-cli/src/filter.rs

@@ -23,7 +23,6 @@ use std::{
 
 use chrono::{Datelike, Local, TimeZone, Utc};
 use log::error;
-use serde_json::Value;
 
 use darkfi::Result;
 
@@ -38,7 +37,8 @@ pub fn apply_filter(tasks: &mut Vec<TaskInfo>, filter: &str) {
         // Filter by state.
         _ if filter.contains("state:") => {
             let kv: Vec<&str> = filter.split(':').collect();
-            if let Some(state) = Value::from(kv[1]).as_str() {
+            //if let Some(state) = Value::from(kv[1]).as_str() {
+            if let Some(state) = Some(kv[1]) {
                 match state {
                     "open" => tasks.retain(|task| task.state == State::Open.to_string()),
                     "start" => tasks.retain(|task| task.state == State::Start.to_string()),
@@ -59,7 +59,8 @@ pub fn apply_filter(tasks: &mut Vec<TaskInfo>, filter: &str) {
         _ if filter.contains("month:") => {
             let kv: Vec<&str> = filter.split(':').collect();
             if kv.len() == 2 {
-                if let Some(value) = Value::from(kv[1]).as_str() {
+                //if let Some(value) = Value::from(kv[1]).as_str() {
+                if let Some(value) = Some(kv[1]) {
                     if value.len() != 4 || value.parse::<u32>().is_err() {
                         error!(
                             "Please provide month date as \"MMYY\" (e.g. 0922 for September 2022)"
@@ -71,7 +72,7 @@ pub fn apply_filter(tasks: &mut Vec<TaskInfo>, filter: &str) {
 
                     let year = year + (Utc::now().year() / 100) * 100;
                     tasks.retain(|task| {
-                        let date = task.created_at;
+                        let date = task.created_at.0;
                         let task_date = Utc.timestamp_nanos(date.try_into().unwrap()).date_naive();
                         task_date.month() == month && task_date.year() == year
                     })
@@ -89,7 +90,8 @@ pub fn apply_filter(tasks: &mut Vec<TaskInfo>, filter: &str) {
         _ if filter.contains("project:") => {
             let kv: Vec<&str> = filter.split(':').collect();
             if kv.len() == 2 {
-                if let Some(value) = Value::from(kv[1]).as_str() {
+                // OLD: if let Some(value) = Value::from(kv[1]).as_str() {
+                if let Some(value) = Some(kv[1]) {
                     if value.is_empty() {
                         tasks.retain(|task| task.project.is_empty())
                     } else {
@@ -128,7 +130,9 @@ pub fn apply_filter(tasks: &mut Vec<TaskInfo>, filter: &str) {
             };
 
             if kv.len() == 2 {
-                if let Some(value) = Value::from(kv[1]).as_str() {
+                // OLD: let Value::from(kv[1]).as_str();
+                let value = Some(kv[1]);
+                if let Some(value) = value {
                     if value.is_empty() {
                         tasks.retain(|task| task.due.is_none())
                     } else {
@@ -140,7 +144,7 @@ pub fn apply_filter(tasks: &mut Vec<TaskInfo>, filter: &str) {
                         };
 
                         tasks.retain(|task| {
-                            let date = task.due.unwrap_or(0);
+                            let date = if let Some(due) = task.due { due.0 } else { 0 };
                             let task_date =
                                 Utc.timestamp_nanos(date.try_into().unwrap()).date_naive();
 
@@ -181,25 +185,25 @@ pub fn no_filter_warn() {
     }
 }
 
-pub fn get_ids(filters: &mut Vec<String>) -> Result<Vec<u64>> {
+pub fn get_ids(filters: &mut Vec<String>) -> Result<Vec<u32>> {
     let mut vec_ids = vec![];
     let mut matching_id = String::new();
     if let Some(index) = filters.iter().position(|t| {
-        t.parse::<u64>().is_ok() || !t.contains(':') && (t.contains(',') || t.contains('-'))
+        t.parse::<u32>().is_ok() || !t.contains(':') && (t.contains(',') || t.contains('-'))
     }) {
         matching_id.push_str(&filters.remove(index));
     }
 
     match matching_id {
-        _ if matching_id.parse::<u64>().is_ok() => {
-            let id = matching_id.parse::<u64>().unwrap();
+        _ if matching_id.parse::<u32>().is_ok() => {
+            let id = matching_id.parse::<u32>().unwrap();
             vec_ids.push(id)
         }
         _ if !matching_id.contains(':') &&
             (matching_id.contains(',') || matching_id.contains('-')) =>
         {
             let num = matching_id.replace(&[',', '-'][..], "");
-            if num.parse::<u64>().is_err() {
+            if num.parse::<u32>().is_err() {
                 error!("Invalid ID number");
                 exit(1)
             }
@@ -209,17 +213,17 @@ pub fn get_ids(filters: &mut Vec<String>) -> Result<Vec<u64>> {
                     if id.contains('-') {
                         let range: Vec<&str> = id.split('-').collect();
                         let range =
-                            range[0].parse::<u64>().unwrap()..=range[1].parse::<u64>().unwrap();
+                            range[0].parse::<u32>().unwrap()..=range[1].parse::<u32>().unwrap();
                         for rid in range {
                             vec_ids.push(rid)
                         }
                     } else {
-                        vec_ids.push(id.parse::<u64>().unwrap())
+                        vec_ids.push(id.parse::<u32>().unwrap())
                     }
                 }
             } else if matching_id.contains('-') {
                 let range: Vec<&str> = matching_id.split('-').collect();
-                let range = range[0].parse::<u64>().unwrap()..=range[1].parse::<u64>().unwrap();
+                let range = range[0].parse::<u32>().unwrap()..=range[1].parse::<u32>().unwrap();
                 for rid in range {
                     vec_ids.push(rid)
                 }

+ 2 - 2
bin/tau/tau-cli/src/main.rs

@@ -42,7 +42,7 @@ use primitives::{task_from_cli, State, TaskEvent};
 use util::{due_as_timestamp, prompt_text};
 use view::{print_task_info, print_task_list};
 
-use crate::primitives::TaskInfo;
+use taud::task_info::TaskInfo;
 
 const DEFAULT_PATH: &str = "~/tau_exported_tasks";
 
@@ -375,7 +375,7 @@ async fn main() -> Result<()> {
                     Some(date) => {
                         let ts =
                             to_naivedate(date.clone())?.and_hms_opt(12, 0, 0).unwrap().timestamp();
-                        let tasks = tau.get_stop_tasks(Some(ts)).await?;
+                        let tasks = tau.get_stop_tasks(Some(ts.try_into().unwrap())).await?;
                         drawdown(date, tasks, assignee)?;
                     }
                     None => {

+ 7 - 108
bin/tau/tau-cli/src/primitives.rs

@@ -16,58 +16,12 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use std::{fmt, str::FromStr};
-
-use darkfi::{util::time::Timestamp, Error, Result};
+use darkfi::{util::time::Timestamp, Result};
 
 use crate::due_as_timestamp;
+pub(crate) use taud::task_info::{State, TaskEvent, TaskInfo};
 
-pub enum State {
-    Open,
-    Start,
-    Pause,
-    Stop,
-}
-
-impl State {
-    pub const fn is_start(&self) -> bool {
-        matches!(*self, Self::Start)
-    }
-    pub const fn is_pause(&self) -> bool {
-        matches!(*self, Self::Pause)
-    }
-    pub const fn is_stop(&self) -> bool {
-        matches!(*self, Self::Stop)
-    }
-}
-
-impl fmt::Display for State {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match self {
-            State::Open => write!(f, "open"),
-            State::Start => write!(f, "start"),
-            State::Stop => write!(f, "stop"),
-            State::Pause => write!(f, "pause"),
-        }
-    }
-}
-
-impl FromStr for State {
-    type Err = Error;
-
-    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
-        let result = match s.to_lowercase().as_str() {
-            "open" => State::Open,
-            "stop" => State::Stop,
-            "start" => State::Start,
-            "pause" => State::Pause,
-            _ => return Err(Error::ParseFailed("unable to parse state")),
-        };
-        Ok(result)
-    }
-}
-
-#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Debug)]
 pub struct BaseTask {
     pub title: String,
     pub tags: Vec<String>,
@@ -78,27 +32,10 @@ pub struct BaseTask {
     pub rank: Option<f32>,
 }
 
-#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
-pub struct TaskInfo {
-    pub ref_id: String,
-    pub workspace: String,
-    pub id: u32,
-    pub title: String,
-    pub tags: Vec<String>,
-    pub desc: String,
-    pub owner: String,
-    pub assign: Vec<String>,
-    pub project: Vec<String>,
-    pub due: Option<u64>,
-    pub rank: Option<f32>,
-    pub created_at: u64,
-    pub state: String,
-    pub events: Vec<TaskEvent>,
-    pub comments: Vec<Comment>,
-}
-
 impl From<BaseTask> for TaskInfo {
     fn from(value: BaseTask) -> Self {
+        let due = if let Some(vd) = value.due { Some(Timestamp(vd)) } else { None };
+
         Self {
             ref_id: String::default(),
             workspace: String::default(),
@@ -109,9 +46,9 @@ impl From<BaseTask> for TaskInfo {
             owner: String::default(),
             assign: value.assign,
             project: value.project,
-            due: value.due,
+            due,
             rank: value.rank,
-            created_at: u64::default(),
+            created_at: Timestamp(u64::default()),
             state: String::default(),
             events: vec![],
             comments: vec![],
@@ -119,44 +56,6 @@ impl From<BaseTask> for TaskInfo {
     }
 }
 
-#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
-pub struct TaskEvent {
-    pub action: String,
-    pub author: String,
-    pub content: String,
-    pub timestamp: Timestamp,
-}
-
-impl std::fmt::Display for TaskEvent {
-    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-        write!(f, "action: {}, timestamp: {}", self.action, self.timestamp)
-    }
-}
-
-impl Default for TaskEvent {
-    fn default() -> Self {
-        Self {
-            action: State::Open.to_string(),
-            author: "".to_string(),
-            content: "".to_string(),
-            timestamp: Timestamp::current_time(),
-        }
-    }
-}
-
-#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
-pub struct Comment {
-    content: String,
-    author: String,
-    timestamp: Timestamp,
-}
-
-impl std::fmt::Display for Comment {
-    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-        write!(f, "{} author: {}, content: {} ", self.timestamp, self.author, self.content)
-    }
-}
-
 pub fn task_from_cli(values: Vec<String>) -> Result<BaseTask> {
     let mut title = String::new();
     let mut tags = vec![];

+ 102 - 39
bin/tau/tau-cli/src/rpc.rs

@@ -16,10 +16,9 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use log::debug;
-use serde_json::{from_value, json};
-
 use darkfi::{rpc::jsonrpc::JsonRequest, Result};
+use log::debug;
+use tinyjson::JsonValue;
 
 use crate::{
     primitives::{BaseTask, State, TaskInfo},
@@ -33,108 +32,172 @@ impl Tau {
 
     /// Add a new task.
     pub async fn add(&self, task: BaseTask) -> Result<u32> {
-        let req = JsonRequest::new("add", json!([task]));
+        let mut params = vec![];
+        params.push(JsonValue::String(task.title.clone()));
+        params.push(JsonValue::Array(
+            task.tags.iter().map(|x| JsonValue::String(x.clone())).collect(),
+        ));
+        params.push(JsonValue::String(task.desc.unwrap_or("".to_string())));
+        params.push(JsonValue::Array(
+            task.assign.iter().map(|x| JsonValue::String(x.clone())).collect(),
+        ));
+        params.push(JsonValue::Array(
+            task.project.iter().map(|x| JsonValue::String(x.clone())).collect(),
+        ));
+
+        let due = if let Some(num) = task.due {
+            JsonValue::String(num.to_string())
+        } else {
+            JsonValue::Null
+        };
+        params.push(due);
+
+        let rank =
+            if let Some(num) = task.rank { JsonValue::Number(num.into()) } else { JsonValue::Null };
+        params.push(rank);
+
+        let req = JsonRequest::new("add", params);
         let rep = self.rpc_client.request(req).await?;
 
         debug!("Got reply: {:?}", rep);
-        let reply: u32 = from_value(rep)?;
-        Ok(reply)
+        Ok(*rep.get::<f64>().unwrap() as u32)
     }
 
     /// Get current open tasks ids.
-    pub async fn get_ids(&self) -> Result<Vec<u64>> {
-        let req = JsonRequest::new("get_ids", json!([]));
+    pub async fn get_ids(&self) -> Result<Vec<u32>> {
+        let req = JsonRequest::new("get_ids", vec![]);
         let rep = self.rpc_client.request(req).await?;
 
+        debug!("Got reply: {:?}", rep);
+
         let mut ret = vec![];
-        for i in rep.as_array().unwrap() {
-            ret.push(i.as_u64().unwrap());
+        for i in rep.get::<Vec<JsonValue>>().unwrap() {
+            ret.push(*i.get::<f64>().unwrap() as u32)
         }
 
         Ok(ret)
     }
 
     /// Update existing task given it's ID and some params.
-    pub async fn update(&self, id: u64, task: BaseTask) -> Result<bool> {
-        let req = JsonRequest::new("update", json!([id, task]));
+    pub async fn update(&self, id: u32, task: BaseTask) -> Result<bool> {
+        let mut params = vec![];
+        params.push(JsonValue::String(task.title.clone()));
+        params.push(JsonValue::Array(
+            task.tags.iter().map(|x| JsonValue::String(x.clone())).collect(),
+        ));
+        params.push(JsonValue::String(task.desc.unwrap_or("".to_string())));
+        params.push(JsonValue::Array(
+            task.assign.iter().map(|x| JsonValue::String(x.clone())).collect(),
+        ));
+        params.push(JsonValue::Array(
+            task.project.iter().map(|x| JsonValue::String(x.clone())).collect(),
+        ));
+
+        let due = if let Some(num) = task.due {
+            JsonValue::String(num.to_string())
+        } else {
+            JsonValue::Null
+        };
+        params.push(due);
+
+        let rank =
+            if let Some(num) = task.rank { JsonValue::Number(num.into()) } else { JsonValue::Null };
+        params.push(rank);
+
+        let req = JsonRequest::new(
+            "update",
+            vec![JsonValue::Number(id.into()), JsonValue::Array(params)],
+        );
         let rep = self.rpc_client.request(req).await?;
 
         debug!("Got reply: {:?}", rep);
-        let reply: bool = from_value(rep)?;
-        Ok(reply)
+        Ok(*rep.get::<bool>().unwrap())
     }
 
     /// Set the state for a task.
-    pub async fn set_state(&self, id: u64, state: &State) -> Result<bool> {
-        let req = JsonRequest::new("set_state", json!([id, state.to_string()]));
+    pub async fn set_state(&self, id: u32, state: &State) -> Result<bool> {
+        let req = JsonRequest::new(
+            "set_state",
+            vec![JsonValue::Number(id.into()), JsonValue::String(state.to_string())],
+        );
         let rep = self.rpc_client.request(req).await?;
 
         debug!("Got reply: {:?}", rep);
-        let reply: bool = from_value(rep)?;
-        Ok(reply)
+        Ok(*rep.get::<bool>().unwrap())
     }
 
     /// Set a comment for a task.
-    pub async fn set_comment(&self, id: u64, content: &str) -> Result<bool> {
-        let req = JsonRequest::new("set_comment", json!([id, content]));
+    pub async fn set_comment(&self, id: u32, content: &str) -> Result<bool> {
+        let req = JsonRequest::new(
+            "set_comment",
+            vec![JsonValue::Number(id.into()), JsonValue::String(content.to_string())],
+        );
         let rep = self.rpc_client.request(req).await?;
 
         debug!("Got reply: {:?}", rep);
-        let reply: bool = from_value(rep)?;
-        Ok(reply)
+        Ok(*rep.get::<bool>().unwrap())
     }
 
     /// Get task data by its ID.
-    pub async fn get_task_by_id(&self, id: u64) -> Result<TaskInfo> {
-        let req = JsonRequest::new("get_task_by_id", json!([id]));
+    pub async fn get_task_by_id(&self, id: u32) -> Result<TaskInfo> {
+        let req = JsonRequest::new("get_task_by_id", vec![JsonValue::Number(id.into())]);
         let rep = self.rpc_client.request(req).await?;
 
-        Ok(serde_json::from_value(rep)?)
+        debug!("Got reply: {:?}", rep);
+        let rep = rep.into();
+        Ok(rep)
     }
 
     /// Get month's stopped tasks.
-    pub async fn get_stop_tasks(&self, month: Option<i64>) -> Result<Vec<TaskInfo>> {
-        let req = JsonRequest::new("get_stop_tasks", json!([month]));
+    pub async fn get_stop_tasks(&self, month: Option<u64>) -> Result<Vec<TaskInfo>> {
+        let param = if let Some(month) = month {
+            JsonValue::String(month.to_string())
+        } else {
+            JsonValue::Null
+        };
+
+        let req = JsonRequest::new("get_stop_tasks", vec![param]);
         let rep = self.rpc_client.request(req).await?;
 
-        Ok(serde_json::from_value(rep)?)
+        debug!("Got reply: {:?}", rep);
+        let rep =
+            rep.get::<Vec<JsonValue>>().unwrap().iter().map(|x| (*x).clone().into()).collect();
+        Ok(rep)
     }
 
     /// Switch workspace.
     pub async fn switch_ws(&self, workspace: String) -> Result<bool> {
-        let req = JsonRequest::new("switch_ws", json!([workspace]));
+        let req = JsonRequest::new("switch_ws", vec![JsonValue::String(workspace)]);
         let rep = self.rpc_client.request(req).await?;
 
         debug!("Got reply: {:?}", rep);
-        let reply: bool = from_value(rep)?;
-        Ok(reply)
+        Ok(*rep.get::<bool>().unwrap())
     }
 
     /// Get current workspace.
     pub async fn get_ws(&self) -> Result<String> {
-        let req = JsonRequest::new("get_ws", json!([]));
+        let req = JsonRequest::new("get_ws", vec![]);
         let rep = self.rpc_client.request(req).await?;
 
-        Ok(serde_json::from_value(rep)?)
+        debug!("Got reply: {:?}", rep);
+        Ok(rep.get::<String>().unwrap().clone())
     }
 
     /// Export tasks.
     pub async fn export_to(&self, path: String) -> Result<bool> {
-        let req = JsonRequest::new("export", json!([path]));
+        let req = JsonRequest::new("export", vec![JsonValue::String(path)]);
         let rep = self.rpc_client.request(req).await?;
 
         debug!("Got reply: {:?}", rep);
-
-        Ok(serde_json::from_value(rep)?)
+        Ok(*rep.get::<bool>().unwrap())
     }
 
     /// Import tasks.
     pub async fn import_from(&self, path: String) -> Result<bool> {
-        let req = JsonRequest::new("import", json!([path]));
+        let req = JsonRequest::new("import", vec![JsonValue::String(path)]);
         let rep = self.rpc_client.request(req).await?;
 
         debug!("Got reply: {:?}", rep);
-
-        Ok(serde_json::from_value(rep)?)
+        Ok(*rep.get::<bool>().unwrap())
     }
 }

+ 13 - 4
bin/tau/tau-cli/src/view.rs

@@ -90,14 +90,18 @@ pub fn print_task_list(tasks: Vec<TaskInfo>, ws: String) -> Result<()> {
             print_tags.push(t)
         }
 
+        let due_ = match task.due {
+            Some(ts) => ts.0,
+            None => 0,
+        };
+
         table.add_row(Row::new(vec![
             Cell::new(&task.id.to_string()).style_spec(gen_style),
             Cell::new(&task.title).style_spec(gen_style),
             Cell::new(&print_tags.join(", ")).style_spec(gen_style),
             Cell::new(&task.project.join(", ")).style_spec(gen_style),
             Cell::new(&task.assign.join(", ")).style_spec(gen_style),
-            Cell::new(&timestamp_to_date(task.due.unwrap_or(0), DateFormat::Date))
-                .style_spec(gen_style),
+            Cell::new(&timestamp_to_date(due_, DateFormat::Date)).style_spec(gen_style),
             if task.rank == max_rank {
                 Cell::new(&rank).style_spec(max_style)
             } else if task.rank == min_rank {
@@ -133,8 +137,13 @@ pub fn print_task_list(tasks: Vec<TaskInfo>, ws: String) -> Result<()> {
 }
 
 pub fn taskinfo_table(taskinfo: TaskInfo) -> Result<Table> {
-    let due = timestamp_to_date(taskinfo.due.unwrap_or(0), DateFormat::Date);
-    let created_at = timestamp_to_date(taskinfo.created_at, DateFormat::DateTime);
+    let due_ = match taskinfo.due {
+        Some(ts) => ts.0,
+        None => 0,
+    };
+
+    let due = timestamp_to_date(due_, DateFormat::Date);
+    let created_at = timestamp_to_date(taskinfo.created_at.0, DateFormat::DateTime);
     let rank = if let Some(r) = taskinfo.rank { r.to_string() } else { "".to_string() };
 
     let mut table = table!(

+ 1 - 1
bin/tau/taud/src/error.rs

@@ -60,7 +60,7 @@ pub fn to_json_result(res: TaudResult<JsonValue>, id: u16) -> JsonResult {
             TaudError::InvalidId => {
                 JsonError::new(ErrorCode::InvalidParams, Some("invalid task id".into()), id).into()
             }
-            TaudError::InvalidData(e) | TaudError::SerdeJsonError(e) => {
+            TaudError::InvalidData(e) | TaudError::JsonError(e) => {
                 JsonError::new(ErrorCode::InvalidParams, Some(e), id).into()
             }
             TaudError::InvalidDueTime => {

+ 221 - 137
bin/tau/taud/src/jsonrpc.rs

@@ -22,8 +22,7 @@ use async_std::sync::Mutex;
 use async_trait::async_trait;
 use crypto_box::ChaChaBox;
 use log::{debug, warn};
-use serde::{Deserialize, Serialize};
-use serde_json::{json, Value};
+use tinyjson::JsonValue;
 
 use darkfi::{
     net,
@@ -51,43 +50,25 @@ pub struct JsonRpcInterface {
     p2p: net::P2pPtr,
 }
 
-#[derive(Clone, Debug, Serialize, Deserialize)]
-struct BaseTaskInfo {
-    title: String,
-    tags: Vec<String>,
-    desc: String,
-    assign: Vec<String>,
-    project: Vec<String>,
-    due: Option<Timestamp>,
-    rank: Option<f32>,
-}
-
 #[async_trait]
 impl RequestHandler for JsonRpcInterface {
     async fn handle_request(&self, req: JsonRequest) -> JsonResult {
-        if !req.params.is_array() {
-            return JsonError::new(ErrorCode::InvalidParams, None, req.id).into()
-        }
-
-        let params = req.params.as_array().unwrap();
-
         let rep = match req.method.as_str() {
-            Some("add") => self.add(params).await,
-            Some("get_ids") => self.get_ids(params).await,
-            Some("update") => self.update(params).await,
-            Some("set_state") => self.set_state(params).await,
-            Some("set_comment") => self.set_comment(params).await,
-            Some("get_task_by_id") => self.get_task_by_id(params).await,
-            Some("switch_ws") => self.switch_ws(params).await,
-            Some("get_ws") => self.get_ws(params).await,
-            Some("export") => self.export_to(params).await,
-            Some("import") => self.import_from(params).await,
-            Some("get_stop_tasks") => self.get_stop_tasks(params).await,
-            Some("ping") => self.pong(params).await,
-
-            Some("dnet_switch") => self.dnet_switch(params).await,
-            Some("dnet_info") => self.dnet_info(params).await,
-            Some(_) | None => return JsonError::new(ErrorCode::MethodNotFound, None, req.id).into(),
+            "add" => self.add(req.params).await,
+            "get_ids" => self.get_ids(req.params).await,
+            "update" => self.update(req.params).await,
+            "set_state" => self.set_state(req.params).await,
+            "set_comment" => self.set_comment(req.params).await,
+            "get_task_by_id" => self.get_task_by_id(req.params).await,
+            "switch_ws" => self.switch_ws(req.params).await,
+            "get_ws" => self.get_ws(req.params).await,
+            "export" => self.export_to(req.params).await,
+            "import" => self.import_from(req.params).await,
+            "get_stop_tasks" => self.get_stop_tasks(req.params).await,
+
+            "ping" => return self.pong(req.id, req.params).await,
+            "dnet_switch" => self.dnet_switch(req.params).await,
+            _ => return JsonError::new(ErrorCode::MethodNotFound, None, req.id).into(),
         };
 
         to_json_result(rep, req.id)
@@ -106,15 +87,6 @@ impl JsonRpcInterface {
         Self { dataset_path, nickname, workspace, workspaces, notify_queue_sender, p2p }
     }
 
-    // RPCAPI:
-    // Replies to a ping method.
-    //
-    // --> {"jsonrpc": "2.0", "method": "ping", "params": [], "id": 42}
-    // <-- {"jsonrpc": "2.0", "result": "pong", "id": 42}
-    async fn pong(&self, _params: &[Value]) -> TaudResult<Value> {
-        Ok(json!("pong"))
-    }
-
     // RPCAPI:
     // Activate or deactivate dnet in the P2P stack.
     // By sending `true`, dnet will be activated, and by sending `false` dnet will
@@ -122,28 +94,21 @@ impl JsonRpcInterface {
     //
     // --> {"jsonrpc": "2.0", "method": "dnet_switch", "params": [true], "id": 42}
     // <-- {"jsonrpc": "2.0", "result": true, "id": 42}
-    async fn dnet_switch(&self, params: &[Value]) -> TaudResult<Value> {
-        if params.len() != 1 && params[0].as_bool().is_none() {
+    async fn dnet_switch(&self, params: JsonValue) -> TaudResult<JsonValue> {
+        let params = params.get::<Vec<JsonValue>>().unwrap();
+        if params.len() != 1 || !params[0].is_bool() {
             return Err(TaudError::InvalidData("Invalid parameters".into()))
         }
 
-        if params[0].as_bool().unwrap() {
+        let switch = params[0].get::<bool>().unwrap();
+
+        if *switch {
             self.p2p.dnet_enable().await;
         } else {
             self.p2p.dnet_disable().await;
         }
 
-        Ok(json!(true))
-    }
-
-    // RPCAPI:
-    // Retrieves P2P network information.
-    //
-    // --> {"jsonrpc": "2.0", "method": "dnet_info", "params": [], "id": 42}
-    // <-- {"jsonrpc": "2.0", result": {"nodeID": [], "nodeinfo": [], "id": 42}
-    async fn dnet_info(&self, _params: &[Value]) -> TaudResult<Value> {
-        let dnet_info = self.p2p.dnet_info().await;
-        Ok(net::P2p::map_dnet_info(dnet_info))
+        Ok(JsonValue::Boolean(true))
     }
 
     // RPCAPI:
@@ -161,79 +126,156 @@ impl JsonRpcInterface {
     //      "id": 1
     //      }
     // <-- {"jsonrpc": "2.0", "result": true, "id": 1}
-    async fn add(&self, params: &[Value]) -> TaudResult<Value> {
+    async fn add(&self, params: JsonValue) -> TaudResult<JsonValue> {
+        let params = params.get::<Vec<JsonValue>>().unwrap();
         debug!(target: "tau", "JsonRpc::add() params {:?}", params);
 
-        let task: BaseTaskInfo = serde_json::from_value(params[0].clone())?;
+        if params.len() != 7 ||
+            !params[0].is_string() ||
+            !params[1].is_array() ||
+            !params[2].is_string() ||
+            !params[3].is_array() ||
+            !params[4].is_array()
+        {
+            return Err(TaudError::InvalidData("Invalid parameters".to_string()))
+        }
+
+        let due = match &params[5] {
+            JsonValue::Null => None,
+            JsonValue::String(u64_str) => match u64::from_str_radix(&u64_str, 10) {
+                Ok(v) => Some(Timestamp(v)),
+                Err(e) => return Err(TaudError::InvalidData(e.to_string())),
+            },
+            _ => return Err(TaudError::InvalidData("Invalid parameters".to_string())),
+        };
+
+        let rank = match params[6] {
+            JsonValue::Null => None,
+            JsonValue::Number(numba) => Some(numba as f32),
+            _ => return Err(TaudError::InvalidData("Invalid parameters".to_string())),
+        };
+
+        let tags = {
+            let mut tags = vec![];
+
+            for val in params[1].get::<Vec<JsonValue>>().unwrap().iter() {
+                if let Some(tag) = val.get::<String>() {
+                    tags.push(tag.clone());
+                } else {
+                    return Err(TaudError::InvalidData("Invalid parameters".to_string()))
+                }
+            }
+
+            tags
+        };
+
+        let assigns = {
+            let mut assigns = vec![];
+
+            for val in params[3].get::<Vec<JsonValue>>().unwrap().iter() {
+                if let Some(assign) = val.get::<String>() {
+                    assigns.push(assign.clone());
+                } else {
+                    return Err(TaudError::InvalidData("Invalid parameters".to_string()))
+                }
+            }
+
+            assigns
+        };
+
+        let projects = {
+            let mut projects = vec![];
+
+            for val in params[4].get::<Vec<JsonValue>>().unwrap().iter() {
+                if let Some(project) = val.get::<String>() {
+                    projects.push(project.clone());
+                } else {
+                    return Err(TaudError::InvalidData("Invalid parameters".to_string()))
+                }
+            }
+
+            projects
+        };
+
         let mut new_task: TaskInfo = TaskInfo::new(
             self.workspace.lock().await.clone(),
-            &task.title,
-            &task.desc,
+            &params[0].get::<String>().unwrap(),
+            &params[2].get::<String>().unwrap(),
             &self.nickname,
-            task.due,
-            task.rank,
+            due,
+            rank,
             &self.dataset_path,
         )?;
-        new_task.set_project(&task.project);
-        new_task.set_assign(&task.assign);
-        new_task.set_tags(&task.tags);
+        new_task.set_project(&projects);
+        new_task.set_assign(&assigns);
+        new_task.set_tags(&tags);
 
         self.notify_queue_sender.send(new_task.clone()).await.map_err(Error::from)?;
-        Ok(json!(new_task.id))
+        Ok(JsonValue::Number(new_task.id.into()))
     }
 
     // RPCAPI:
     // List tasks
     // --> {"jsonrpc": "2.0", "method": "get_ids", "params": [], "id": 1}
     // <-- {"jsonrpc": "2.0", "result": [task_id, ...], "id": 1}
-    async fn get_ids(&self, params: &[Value]) -> TaudResult<Value> {
+    async fn get_ids(&self, params: JsonValue) -> TaudResult<JsonValue> {
+        let params = params.get::<Vec<JsonValue>>().unwrap();
         debug!(target: "tau", "JsonRpc::get_ids() params {:?}", params);
 
         let ws = self.workspace.lock().await.clone();
         let tasks = MonthTasks::load_current_tasks(&self.dataset_path, ws, false)?;
 
-        let task_ids: Vec<u32> = tasks.iter().map(|task| task.get_id()).collect();
+        let task_ids: Vec<JsonValue> =
+            tasks.iter().map(|task| JsonValue::Number(task.get_id().into())).collect();
 
-        Ok(json!(task_ids))
+        Ok(JsonValue::Array(task_ids))
     }
 
     // RPCAPI:
     // Update task and returns `true` upon success.
     // --> {"jsonrpc": "2.0", "method": "update", "params": [task_id, {"title": "new title"} ], "id": 1}
     // <-- {"jsonrpc": "2.0", "result": true, "id": 1}
-    async fn update(&self, params: &[Value]) -> TaudResult<Value> {
+    async fn update(&self, params: JsonValue) -> TaudResult<JsonValue> {
+        let params = params.get::<Vec<JsonValue>>().unwrap();
         debug!(target: "tau", "JsonRpc::update() params {:?}", params);
 
-        if params.len() != 2 {
+        if params.len() != 2 || !params[0].is_number() || !params[1].is_object() {
             return Err(TaudError::InvalidData("len of params should be 2".into()))
         }
 
         let ws = self.workspace.lock().await.clone();
-        let task = self.check_params_for_update(&params[0], &params[1], ws)?;
+
+        let task = self.check_params_for_update(
+            *params[0].get::<f64>().unwrap() as u32,
+            params[1].get::<HashMap<String, JsonValue>>().unwrap(),
+            ws,
+        )?;
 
         self.notify_queue_sender.send(task).await.map_err(Error::from)?;
 
-        Ok(json!(true))
+        Ok(JsonValue::Boolean(true))
     }
 
     // RPCAPI:
     // Set state for a task and returns `true` upon success.
     // --> {"jsonrpc": "2.0", "method": "set_state", "params": [task_id, state], "id": 1}
     // <-- {"jsonrpc": "2.0", "result": true, "id": 1}
-    async fn set_state(&self, params: &[Value]) -> TaudResult<Value> {
+    async fn set_state(&self, params: JsonValue) -> TaudResult<JsonValue> {
         // Allowed states for a task
         let states = ["stop", "start", "open", "pause"];
 
+        let params = params.get::<Vec<JsonValue>>().unwrap();
         debug!(target: "tau", "JsonRpc::set_state() params {:?}", params);
 
-        if params.len() != 2 {
+        if params.len() != 2 || !params[0].is_number() || !params[1].is_string() {
             return Err(TaudError::InvalidData("len of params should be 2".into()))
         }
 
-        let state: String = serde_json::from_value(params[1].clone())?;
+        let state = params[1].get::<String>().unwrap();
         let ws = self.workspace.lock().await.clone();
 
-        let mut task: TaskInfo = self.load_task_by_id(&params[0], ws)?;
+        let mut task: TaskInfo =
+            self.load_task_by_id(*params[0].get::<f64>().unwrap() as u32, ws)?;
 
         if states.contains(&state.as_str()) {
             task.set_state(&state);
@@ -242,73 +284,90 @@ impl JsonRpcInterface {
 
         self.notify_queue_sender.send(task).await.map_err(Error::from)?;
 
-        Ok(json!(true))
+        Ok(JsonValue::Boolean(true))
     }
 
     // RPCAPI:
     // Set comment for a task and returns `true` upon success.
     // --> {"jsonrpc": "2.0", "method": "set_comment", "params": [task_id, comment_content], "id": 1}
     // <-- {"jsonrpc": "2.0", "result": true, "id": 1}
-    async fn set_comment(&self, params: &[Value]) -> TaudResult<Value> {
+    async fn set_comment(&self, params: JsonValue) -> TaudResult<JsonValue> {
+        let params = params.get::<Vec<JsonValue>>().unwrap();
         debug!(target: "tau", "JsonRpc::set_comment() params {:?}", params);
 
-        if params.len() != 2 {
+        if params.len() != 2 || !params[0].is_number() || !params[1].is_string() {
             return Err(TaudError::InvalidData("len of params should be 2".into()))
         }
 
-        let comment_content: String = serde_json::from_value(params[1].clone())?;
+        let id = *params[0].get::<f64>().unwrap() as u32;
+        let comment_content = params[1].get::<String>().unwrap();
 
         let ws = self.workspace.lock().await.clone();
-        let mut task: TaskInfo = self.load_task_by_id(&params[0], ws)?;
+        let mut task: TaskInfo = self.load_task_by_id(id, ws)?;
 
         task.set_comment(Comment::new(&comment_content, &self.nickname));
         set_event(&mut task, "comment", &self.nickname, &comment_content);
 
         self.notify_queue_sender.send(task).await.map_err(Error::from)?;
 
-        Ok(json!(true))
+        Ok(JsonValue::Boolean(true))
     }
 
     // RPCAPI:
     // Get a task by id.
     // --> {"jsonrpc": "2.0", "method": "get_task_by_id", "params": [task_id], "id": 1}
     // <-- {"jsonrpc": "2.0", "result": "task", "id": 1}
-    async fn get_task_by_id(&self, params: &[Value]) -> TaudResult<Value> {
+    async fn get_task_by_id(&self, params: JsonValue) -> TaudResult<JsonValue> {
+        let params = params.get::<Vec<JsonValue>>().unwrap();
         debug!(target: "tau", "JsonRpc::get_task_by_id() params {:?}", params);
 
-        if params.len() != 1 {
+        if params.len() != 1 || !params[0].is_number() {
             return Err(TaudError::InvalidData("len of params should be 1".into()))
         }
 
         let ws = self.workspace.lock().await.clone();
-        let task: TaskInfo = self.load_task_by_id(&params[0], ws)?;
+        let task: TaskInfo = self.load_task_by_id(*params[0].get::<f64>().unwrap() as u32, ws)?;
+        let task: JsonValue = (&task).into();
 
-        Ok(json!(task))
+        Ok(task)
     }
 
     // RPCAPI:
     // Get all tasks.
     // --> {"jsonrpc": "2.0", "method": "get_stop_tasks", "params": [task_id], "id": 1}
     // <-- {"jsonrpc": "2.0", "result": "task", "id": 1}
-    async fn get_stop_tasks(&self, params: &[Value]) -> TaudResult<Value> {
+    async fn get_stop_tasks(&self, params: JsonValue) -> TaudResult<JsonValue> {
+        let params = params.get::<Vec<JsonValue>>().unwrap();
         debug!(target: "tau", "JsonRpc::get_stop_tasks() params {:?}", params);
 
-        if params.len() != 1 {
+        if params.len() != 1 || !params[0].is_string() {
             return Err(TaudError::InvalidData("len of params should be 1".into()))
         }
-        let month = params[0].as_u64().map(Timestamp);
+
+        let month = match params[0].get::<String>() {
+            Some(u64_str) => match u64::from_str_radix(u64_str, 10) {
+                Ok(v) => Some(Timestamp(v)),
+                //Err(e) => return Err(TaudError::InvalidData(e.to_string())),
+                Err(_) => None,
+            },
+
+            None => None,
+        };
+
         let ws = self.workspace.lock().await.clone();
 
         let tasks = MonthTasks::load_stop_tasks(&self.dataset_path, ws, month.as_ref())?;
+        let tasks: Vec<JsonValue> = tasks.iter().map(|x| x.into()).collect();
 
-        Ok(json!(tasks))
+        Ok(JsonValue::Array(tasks))
     }
 
     // RPCAPI:
     // Switch tasks workspace.
     // --> {"jsonrpc": "2.0", "method": "switch_ws", "params": [workspace], "id": 1}
     // <-- {"jsonrpc": "2.0", "result": "true", "id": 1}
-    async fn switch_ws(&self, params: &[Value]) -> TaudResult<Value> {
+    async fn switch_ws(&self, params: JsonValue) -> TaudResult<JsonValue> {
+        let params = params.get::<Vec<JsonValue>>().unwrap();
         debug!(target: "tau", "JsonRpc::switch_ws() params {:?}", params);
 
         if params.len() != 1 {
@@ -319,34 +378,36 @@ impl JsonRpcInterface {
             return Err(TaudError::InvalidData("Invalid workspace".into()))
         }
 
-        let ws = params[0].as_str().unwrap().to_string();
+        let ws = params[0].get::<String>().unwrap();
         let mut s = self.workspace.lock().await;
 
-        if self.workspaces.contains_key(&ws) {
-            *s = ws
+        if self.workspaces.contains_key(ws) {
+            *s = ws.to_string()
         } else {
             warn!("Workspace \"{}\" is not configured", ws);
-            return Ok(json!(false))
+            return Ok(JsonValue::Boolean(false))
         }
 
-        Ok(json!(true))
+        Ok(JsonValue::Boolean(true))
     }
 
     // RPCAPI:
     // Get workspace.
     // --> {"jsonrpc": "2.0", "method": "get_ws", "params": [], "id": 1}
     // <-- {"jsonrpc": "2.0", "result": "workspace", "id": 1}
-    async fn get_ws(&self, params: &[Value]) -> TaudResult<Value> {
+    async fn get_ws(&self, params: JsonValue) -> TaudResult<JsonValue> {
+        let params = params.get::<Vec<JsonValue>>().unwrap();
         debug!(target: "tau", "JsonRpc::get_ws() params {:?}", params);
         let ws = self.workspace.lock().await.clone();
-        Ok(json!(ws))
+        Ok(JsonValue::String(ws))
     }
 
     // RPCAPI:
     // Export tasks.
     // --> {"jsonrpc": "2.0", "method": "export_to", "params": [path], "id": 1}
     // <-- {"jsonrpc": "2.0", "result": "true", "id": 1}
-    async fn export_to(&self, params: &[Value]) -> TaudResult<Value> {
+    async fn export_to(&self, params: JsonValue) -> TaudResult<JsonValue> {
+        let params = params.get::<Vec<JsonValue>>().unwrap();
         debug!(target: "tau", "JsonRpc::export_to() params {:?}", params);
 
         if params.len() != 1 {
@@ -358,7 +419,8 @@ impl JsonRpcInterface {
         }
 
         // mkdir datastore_path if not exists
-        let path = expand_path(params[0].as_str().unwrap())?.join("exported_tasks");
+        let path = params[0].get::<String>().unwrap();
+        let path = expand_path(path)?.join("exported_tasks");
         create_dir_all(path.join("month")).map_err(Error::from)?;
         create_dir_all(path.join("task")).map_err(Error::from)?;
 
@@ -369,14 +431,15 @@ impl JsonRpcInterface {
             task.save(&path)?;
         }
 
-        Ok(json!(true))
+        Ok(JsonValue::Boolean(true))
     }
 
     // RPCAPI:
     // Import tasks.
     // --> {"jsonrpc": "2.0", "method": "import_from", "params": [path], "id": 1}
     // <-- {"jsonrpc": "2.0", "result": "true", "id": 1}
-    async fn import_from(&self, params: &[Value]) -> TaudResult<Value> {
+    async fn import_from(&self, params: JsonValue) -> TaudResult<JsonValue> {
+        let params = params.get::<Vec<JsonValue>>().unwrap();
         debug!(target: "tau", "JsonRpc::import_from() params {:?}", params);
 
         if params.len() != 1 {
@@ -387,7 +450,8 @@ impl JsonRpcInterface {
             return Err(TaudError::InvalidData("Invalid path".into()))
         }
 
-        let path = expand_path(params[0].as_str().unwrap())?.join("exported_tasks");
+        let path = params[0].get::<String>().unwrap();
+        let path = expand_path(path)?.join("exported_tasks");
         let ws = self.workspace.lock().await.clone();
 
         let mut task_ids: Vec<u32> =
@@ -411,34 +475,26 @@ impl JsonRpcInterface {
             task_ids.push(task.id);
             self.notify_queue_sender.send(task).await.map_err(Error::from)?;
         }
-        Ok(json!(true))
+        Ok(JsonValue::Boolean(true))
     }
 
-    fn load_task_by_id(&self, task_id: &Value, ws: String) -> TaudResult<TaskInfo> {
-        let task_id: u64 = serde_json::from_value(task_id.clone())?;
+    fn load_task_by_id(&self, task_id: u32, ws: String) -> TaudResult<TaskInfo> {
         let tasks = MonthTasks::load_current_tasks(&self.dataset_path, ws, false)?;
-        let task = tasks.into_iter().find(|t| (t.get_id() as u64) == task_id);
+        let task = tasks.into_iter().find(|t| (t.get_id()) == task_id);
 
         task.ok_or(TaudError::InvalidId)
     }
 
     fn check_params_for_update(
         &self,
-        task_id: &Value,
-        fields: &Value,
+        task_id: u32,
+        fields: &HashMap<String, JsonValue>,
         ws: String,
     ) -> TaudResult<TaskInfo> {
         let mut task: TaskInfo = self.load_task_by_id(task_id, ws)?;
 
-        if !fields.is_object() {
-            return Err(TaudError::InvalidData("Invalid task's data".into()))
-        }
-
-        let fields = fields.as_object().unwrap();
-
         if fields.contains_key("title") {
-            let title = fields.get("title").unwrap().clone();
-            let title: String = serde_json::from_value(title)?;
+            let title = fields["title"].get::<String>().unwrap();
             if !title.is_empty() {
                 task.set_title(&title);
                 set_event(&mut task, "title", &self.nickname, &title);
@@ -446,19 +502,23 @@ impl JsonRpcInterface {
         }
 
         if fields.contains_key("desc") {
-            let description = fields.get("desc");
-            if let Some(description) = description {
-                let description: Option<String> = serde_json::from_value(description.clone())?;
-                if let Some(desc) = description {
-                    task.set_desc(&desc);
-                    set_event(&mut task, "desc", &self.nickname, &desc);
-                }
+            let desc = fields["desc"].get::<String>().unwrap();
+            if !desc.is_empty() {
+                task.set_desc(&desc);
+                set_event(&mut task, "desc", &self.nickname, &desc);
             }
         }
 
         if fields.contains_key("rank") {
-            let rank_opt = fields.get("rank").unwrap();
-            let rank: Option<Option<f32>> = serde_json::from_value(rank_opt.clone())?;
+            // TODO: Why is this a double Option?
+            let rank = {
+                match fields["rank"] {
+                    JsonValue::Null => None,
+                    JsonValue::Number(rank) => Some(Some(rank as f32)),
+                    _ => unreachable!(),
+                }
+            };
+
             if let Some(rank) = rank {
                 task.set_rank(rank);
                 match rank {
@@ -473,8 +533,17 @@ impl JsonRpcInterface {
         }
 
         if fields.contains_key("due") {
-            let due = fields.get("due").unwrap().clone();
-            let due: Option<Option<Timestamp>> = serde_json::from_value(due)?;
+            // TODO: Why is this a double Option?
+            let due = {
+                match &fields["due"] {
+                    JsonValue::Null => None,
+                    JsonValue::String(ts_str) => {
+                        Some(Some(Timestamp(u64::from_str_radix(&ts_str, 10).unwrap())))
+                    }
+                    _ => unreachable!(),
+                }
+            };
+
             if let Some(d) = due {
                 task.set_due(d);
                 match d {
@@ -489,8 +558,13 @@ impl JsonRpcInterface {
         }
 
         if fields.contains_key("assign") {
-            let assign = fields.get("assign").unwrap().clone();
-            let assign: Vec<String> = serde_json::from_value(assign)?;
+            let assign: Vec<String> = fields["assign"]
+                .get::<Vec<JsonValue>>()
+                .unwrap()
+                .iter()
+                .map(|x| x.get::<String>().unwrap().clone())
+                .collect();
+
             if !assign.is_empty() {
                 task.set_assign(&assign);
                 set_event(&mut task, "assign", &self.nickname, &assign.join(", "));
@@ -498,8 +572,13 @@ impl JsonRpcInterface {
         }
 
         if fields.contains_key("project") {
-            let project = fields.get("project").unwrap().clone();
-            let project: Vec<String> = serde_json::from_value(project)?;
+            let project: Vec<String> = fields["project"]
+                .get::<Vec<JsonValue>>()
+                .unwrap()
+                .iter()
+                .map(|x| x.get::<String>().unwrap().clone())
+                .collect();
+
             if !project.is_empty() {
                 task.set_project(&project);
                 set_event(&mut task, "project", &self.nickname, &project.join(", "));
@@ -507,8 +586,13 @@ impl JsonRpcInterface {
         }
 
         if fields.contains_key("tags") {
-            let tags = fields.get("tags").unwrap().clone();
-            let tags: Vec<String> = serde_json::from_value(tags)?;
+            let tags: Vec<String> = fields["tags"]
+                .get::<Vec<JsonValue>>()
+                .unwrap()
+                .iter()
+                .map(|x| x.get::<String>().unwrap().clone())
+                .collect();
+
             if !tags.is_empty() {
                 task.set_tags(&tags);
                 set_event(&mut task, "tags", &self.nickname, &tags.join(", "));

+ 9 - 8
bin/tau/taud/src/main.rs

@@ -39,6 +39,7 @@ use futures::{select, FutureExt};
 use log::{debug, error, info};
 use rand::rngs::OsRng;
 use structopt_toml::StructOptToml;
+use tinyjson::JsonValue;
 
 use darkfi::{
     async_daemonize,
@@ -218,31 +219,31 @@ async fn on_receive_task(
             // otherwise it's a modification.
             match TaskInfo::load(&task.ref_id, datastore_path) {
                 Ok(loaded_task) => {
-                    let loaded_events = loaded_task.events.0;
-                    let mut events = task.events.0.clone();
+                    let loaded_events = loaded_task.events;
+                    let mut events = task.events.clone();
                     events.retain(|ev| !loaded_events.contains(ev));
 
                     let file = "/tmp/tau_pipe";
                     let mut pipe_write = pipe_write(file)?;
                     let mut task_clone = task.clone();
-                    task_clone.events.0 = events;
+                    task_clone.events = events;
 
-                    let json = serde_json::to_string(&task_clone).unwrap();
-                    pipe_write.write_all(json.as_bytes())?;
+                    let json: JsonValue = (&task_clone).into();
+                    pipe_write.write_all(json.stringify().unwrap().as_bytes())?;
                 }
                 Err(_) => {
                     let file = "/tmp/tau_pipe";
                     let mut pipe_write = pipe_write(file)?;
                     let mut task_clone = task.clone();
 
-                    task_clone.events.0.push(TaskEvent::new(
+                    task_clone.events.push(TaskEvent::new(
                         "add_task".to_string(),
                         task_clone.owner.clone(),
                         "".to_string(),
                     ));
 
-                    let json = serde_json::to_string(&task_clone).unwrap();
-                    pipe_write.write_all(json.as_bytes())?;
+                    let json: JsonValue = (&task_clone).into();
+                    pipe_write.write_all(json.stringify().unwrap().as_bytes())?;
                 }
             }
         }

+ 81 - 7
bin/tau/taud/src/task_info.rs

@@ -16,7 +16,12 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use std::path::{Path, PathBuf};
+use std::{
+    collections::HashMap,
+    fmt,
+    path::{Path, PathBuf},
+    str::FromStr,
+};
 
 use darkfi_serial::{SerialDecodable, SerialEncodable};
 use log::debug;
@@ -28,6 +33,7 @@ use darkfi::{
         file::{load_json_file, save_json_file},
         time::Timestamp,
     },
+    Error,
 };
 
 use crate::{
@@ -36,6 +42,51 @@ use crate::{
     util::find_free_id,
 };
 
+pub enum State {
+    Open,
+    Start,
+    Pause,
+    Stop,
+}
+
+impl State {
+    pub const fn is_start(&self) -> bool {
+        matches!(*self, Self::Start)
+    }
+    pub const fn is_pause(&self) -> bool {
+        matches!(*self, Self::Pause)
+    }
+    pub const fn is_stop(&self) -> bool {
+        matches!(*self, Self::Stop)
+    }
+}
+
+impl fmt::Display for State {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            State::Open => write!(f, "open"),
+            State::Start => write!(f, "start"),
+            State::Stop => write!(f, "stop"),
+            State::Pause => write!(f, "pause"),
+        }
+    }
+}
+
+impl FromStr for State {
+    type Err = Error;
+
+    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
+        let result = match s.to_lowercase().as_str() {
+            "open" => State::Open,
+            "stop" => State::Stop,
+            "start" => State::Start,
+            "pause" => State::Pause,
+            _ => return Err(Error::ParseFailed("unable to parse state")),
+        };
+        Ok(result)
+    }
+}
+
 #[derive(Clone, Debug, SerialEncodable, SerialDecodable, PartialEq, Eq)]
 pub struct TaskEvent {
     pub action: String,
@@ -44,6 +95,23 @@ pub struct TaskEvent {
     pub timestamp: Timestamp,
 }
 
+impl std::fmt::Display for TaskEvent {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        write!(f, "action: {}, timestamp: {}", self.action, self.timestamp)
+    }
+}
+
+impl Default for TaskEvent {
+    fn default() -> Self {
+        Self {
+            action: State::Open.to_string(),
+            author: "".to_string(),
+            content: "".to_string(),
+            timestamp: Timestamp::current_time(),
+        }
+    }
+}
+
 impl TaskEvent {
     pub fn new(action: String, author: String, content: String) -> Self {
         Self { action, author, content, timestamp: Timestamp::current_time() }
@@ -65,9 +133,9 @@ impl From<&JsonValue> for TaskEvent {
     fn from(value: &JsonValue) -> TaskEvent {
         let map = value.get::<HashMap<String, JsonValue>>().unwrap();
         TaskEvent {
-            action: map["action"].get().unwrap().clone(),
-            author: map["author"].get().unwrap().clone(),
-            content: map["content"].get().unwrap().clone(),
+            action: map["action"].get::<String>().unwrap().clone(),
+            author: map["author"].get::<String>().unwrap().clone(),
+            content: map["content"].get::<String>().unwrap().clone(),
             timestamp: Timestamp(
                 u64::from_str_radix(map["timestamp"].get::<String>().unwrap(), 10).unwrap(),
             ),
@@ -82,6 +150,12 @@ pub struct Comment {
     timestamp: Timestamp,
 }
 
+impl std::fmt::Display for Comment {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        write!(f, "{} author: {}, content: {} ", self.timestamp, self.author, self.content)
+    }
+}
+
 impl From<Comment> for JsonValue {
     fn from(comment: Comment) -> JsonValue {
         JsonValue::Object(HashMap::from([
@@ -96,8 +170,8 @@ impl From<JsonValue> for Comment {
     fn from(value: JsonValue) -> Comment {
         let map = value.get::<HashMap<String, JsonValue>>().unwrap();
         Comment {
-            content: map["content"].get().unwrap().clone(),
-            author: map["author"].get().unwrap().clone(),
+            content: map["content"].get::<String>().unwrap().clone(),
+            author: map["author"].get::<String>().unwrap().clone(),
             timestamp: Timestamp(
                 u64::from_str_radix(map["timestamp"].get::<String>().unwrap(), 10).unwrap(),
             ),
@@ -218,7 +292,7 @@ impl From<JsonValue> for TaskInfo {
         };
 
         let events: Vec<TaskEvent> = events.iter().map(|x| x.into()).collect();
-        let comments: Vec<Comment> = comments.iter().map(|x| (*x).into()).collect();
+        let comments: Vec<Comment> = comments.iter().map(|x| (*x).clone().into()).collect();
 
         TaskInfo {
             ref_id: value["ref_id"].get::<String>().unwrap().clone(),

+ 7 - 3
src/rpc/jsonrpc.rs

@@ -155,9 +155,13 @@ pub struct JsonRequest {
 impl JsonRequest {
     /// Create a new [`JsonRequest`] object with the given method and parameters.
     /// The request ID is chosen randomly.
-    pub fn new(method: &str, params: JsonValue) -> Self {
-        assert!(params.is_array());
-        Self { jsonrpc: "2.0", id: OsRng::gen(&mut OsRng), method: method.to_string(), params }
+    pub fn new(method: &str, params: Vec<JsonValue>) -> Self {
+        Self {
+            jsonrpc: "2.0",
+            id: OsRng::gen(&mut OsRng),
+            method: method.to_string(),
+            params: JsonValue::Array(params),
+        }
     }
 
     /// Convert the object into a JSON string