Przeglądaj źródła

bin/taud: update 'list' function & add update rpc function

ghassmo 4 lat temu
rodzic
commit
6027d98136
2 zmienionych plików z 195 dodań i 32 usunięć
  1. 157 10
      bin/taud/src/main.rs
  2. 38 22
      bin/taud/src/task_info.rs

+ 157 - 10
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<Vec<TaskInfo>> = 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<Vec<TaskInfo>> {
+            let tasks: Vec<TaskInfo>;
+
+            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<TaskInfo>;
+
+        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<Executor<'_>>) -> Result<()> {

+ 38 - 22
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<Self> {
         let mut task = crate::util::load::<Self>(&Self::get_path(ref_id, settings))?;
         task.set_settings(settings);
@@ -109,28 +97,28 @@ impl TaskInfo {
         crate::util::save::<Self>(&Self::get_path(&self.ref_id, &self.settings), self)
     }
 
-    pub fn get_state(&self) -> String {
-        if let Some(ev) = self.events.last() {
-            return ev.action.clone()
-        } else {
-            return "open".into()
-        }
-    }
-
     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();
+    pub fn get_state(&self) -> String {
+        if let Some(ev) = self.events.last() {
+            return ev.action.clone()
+        } else {
+            return "open".into()
+        }
     }
 
     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<String>) {
+        self.assign = assign.clone();
+    }
+
+    pub fn set_project(&mut self, project: &Vec<String>) {
+        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<Timestamp>) {
+        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