diff --git a/bin/taud/src/main.rs b/bin/taud/src/main.rs index bbbc75835..3534e4dff 100644 --- a/bin/taud/src/main.rs +++ b/bin/taud/src/main.rs @@ -44,6 +44,7 @@ impl RequestHandler for JsonRpcInterface { match req.method.as_str() { Some("add") => return self.add(req.id, req.params).await, Some("list") => return self.list(req.id, req.params).await, + Some("update") => return self.update(req.id, req.params).await, Some(_) | None => return JsonResult::Err(jsonerr(MethodNotFound, None, req.id)), } } @@ -102,16 +103,26 @@ impl JsonRpcInterface { let assign = args[2].as_array(); if assign.is_some() && assign.unwrap().len() > 0 { - for a in assign.unwrap() { - task.assign(a.as_str().unwrap()); - } + task.set_assign( + &assign + .unwrap() + .into_iter() + .filter(|a| a.as_str().is_some()) + .map(|a| a.as_str().unwrap().to_string()) + .collect(), + ); } let project = args[3].as_array(); if project.is_some() && project.unwrap().len() > 0 { - for p in project.unwrap() { - task.project(p.as_str().unwrap()); - } + task.set_project( + &project + .unwrap() + .into_iter() + .filter(|p| p.as_str().is_some()) + .map(|p| p.as_str().unwrap().to_string()) + .collect(), + ); } let result = || -> Result<()> { @@ -128,16 +139,152 @@ impl JsonRpcInterface { // RPCAPI: // List tasks - // --> {"jsonrpc": "2.0", "method": "list", "params": [], "id": 1} + // --> {"jsonrpc": "2.0", "method": "list", "params": [month_date], "id": 1} // <-- {"jsonrpc": "2.0", "result": [task, ...], "id": 1} - async fn list(&self, id: Value, _params: Value) -> JsonResult { - let tasks: Result> = MonthTasks::load_current_open_tasks(&self.settings); + async fn list(&self, id: Value, params: Value) -> JsonResult { + let args = params.as_array().unwrap(); - match tasks { + if args.len() != 1 { + return JsonResult::Err(jsonerr(InvalidParams, None, id)) + } + + let result = || -> Result> { + let tasks: Vec; + + if args[0].is_i64() { + tasks = MonthTasks::load_or_create( + &Timestamp(args[0].as_i64().unwrap()), + &self.settings, + )? + .objects()?; + } else { + tasks = MonthTasks::load_current_open_tasks(&self.settings)?; + } + + Ok(tasks) + }; + + match result() { Ok(tks) => JsonResult::Resp(jsonresp(json!(tks), id)), Err(e) => JsonResult::Err(jsonerr(ServerError(-32603), Some(e.to_string()), id)), } } + + // RPCAPI: + // Update task returns `true` upon success. + // --> {"jsonrpc": "2.0", "method": "id", "params": [task_id, {"title": "new title"} ], "id": 1} + // <-- {"jsonrpc": "2.0", "result": true, "id": 1} + async fn update(&self, id: Value, params: Value) -> JsonResult { + let args = params.as_array().unwrap(); + + if args.len() != 2 { + return JsonResult::Err(jsonerr(InvalidParams, None, id)) + } + + let tasks: Vec; + + if !args[0].is_u64() { + return JsonResult::Err(jsonerr(InvalidParams, Some("invalid id".into()), id)) + } + + if !args[1].is_object() { + return JsonResult::Err(jsonerr(InvalidParams, Some("invalid update data".into()), id)) + } + + let task_id = args[0].as_u64().unwrap(); + let data = args[0].as_object().unwrap(); + + match MonthTasks::load_current_open_tasks(&self.settings) { + Ok(tks) => tasks = tks, + Err(_) => return JsonResult::Err(jsonerr(InvalidParams, None, id)), + } + + let task = tasks.into_iter().find(|t| (t.get_id() as u64) == task_id); + + if task.is_none() { + return JsonResult::Err(jsonerr( + InvalidRequest, + Some("Didn't find a task with the provided id".into()), + id, + )) + } + + let mut task = task.unwrap(); + + let mut result = || -> std::result::Result<(), &str> { + if data.contains_key("title") { + let title = data + .get("title") + .ok_or("error parsing title")? + .as_str() + .ok_or("invalid value for title")?; + task.set_title(title); + } + + if data.contains_key("description") { + let description = data + .get("description") + .ok_or("error parsing description")? + .as_str() + .ok_or("invalid value for description")?; + task.set_desc(description); + } + + if data.contains_key("rank") { + let rank = data + .get("rank") + .ok_or("error parsing rank")? + .as_u64() + .ok_or("invalid value for rank")?; + + task.set_rank(rank as u32); + } + + if data.contains_key("due") { + let due = Some(Timestamp( + data.get("due") + .ok_or("error parsing rank")? + .as_i64() + .ok_or("invalid value for rank")?, + )); + task.set_due(due); + } + + if data.contains_key("assign") { + task.set_assign( + &data + .get("assign") + .ok_or("error parsing assign")? + .as_array() + .ok_or("invalid value for assign")? + .into_iter() + .filter(|a| a.as_str().is_some()) + .map(|a| a.as_str().unwrap().to_string()) + .collect(), + ); + } + + if data.contains_key("project") { + task.set_project( + &data + .get("project") + .ok_or("error parsing project")? + .as_array() + .ok_or("invalid value for project")? + .into_iter() + .filter(|p| p.as_str().is_some()) + .map(|p| p.as_str().unwrap().to_string()) + .collect(), + ); + } + Ok(()) + }; + + match result() { + Ok(()) => JsonResult::Resp(jsonresp(json!(true), id)), + Err(e) => JsonResult::Err(jsonerr(InvalidParams, Some(e.to_string()), id)), + } + } } async fn start(config: TauConfig, executor: Arc>) -> Result<()> { diff --git a/bin/taud/src/task_info.rs b/bin/taud/src/task_info.rs index 8b14b6301..02ff18111 100644 --- a/bin/taud/src/task_info.rs +++ b/bin/taud/src/task_info.rs @@ -87,18 +87,6 @@ impl TaskInfo { }) } - pub fn assign(&mut self, n: &str) { - self.assign.push(n.into()); - } - - pub fn project(&mut self, p: &str) { - self.project.push(p.into()); - } - - pub fn set_comment(&mut self, c: Comment) { - self.comments.push(c); - } - pub fn load(ref_id: &str, settings: &Settings) -> Result { let mut task = crate::util::load::(&Self::get_path(ref_id, settings))?; task.set_settings(settings); @@ -109,6 +97,12 @@ impl TaskInfo { crate::util::save::(&Self::get_path(&self.ref_id, &self.settings), self) } + pub fn activate(&self) -> Result<()> { + let mut mt = MonthTasks::load_or_create(&self.created_at, &self.settings)?; + mt.add(&self.ref_id); + mt.save() + } + pub fn get_state(&self) -> String { if let Some(ev) = self.events.last() { return ev.action.clone() @@ -117,20 +111,14 @@ impl TaskInfo { } } - pub fn activate(&self) -> Result<()> { - let mut mt = MonthTasks::load_or_create(&self.created_at, &self.settings)?; - mt.add(&self.ref_id); - mt.save() - } - - pub fn set_settings(&mut self, settings: &Settings) { - self.settings = settings.clone(); - } - fn get_path(ref_id: &str, settings: &Settings) -> PathBuf { settings.dataset_path.join("task").join(ref_id) } + pub fn get_id(&self) -> u32 { + self.id.clone() + } + pub fn get_ref_id(&self) -> String { self.ref_id.clone() } @@ -139,6 +127,34 @@ impl TaskInfo { self.title = title.into(); } + pub fn set_desc(&mut self, desc: &str) { + self.desc = desc.into(); + } + + pub fn set_assign(&mut self, assign: &Vec) { + self.assign = assign.clone(); + } + + pub fn set_project(&mut self, project: &Vec) { + self.project = project.clone(); + } + + pub fn set_comment(&mut self, c: Comment) { + self.comments.push(c); + } + + pub fn set_rank(&mut self, r: u32) { + self.rank = r; + } + + pub fn set_due(&mut self, d: Option) { + self.due = d; + } + + pub fn set_settings(&mut self, settings: &Settings) { + self.settings = settings.clone(); + } + pub fn set_state(&mut self, action: &str) { if self.get_state() == action { return