Forráskód Böngészése

bin/taud: add generic load/save functions for json dataset

ghassmo 4 éve
szülő
commit
81def80d17
1 módosított fájl, 132 hozzáadás és 13 törlés
  1. 132 13
      bin/taud/src/main.rs

+ 132 - 13
bin/taud/src/main.rs

@@ -1,4 +1,9 @@
-use std::{fs::create_dir_all, path::PathBuf, sync::Arc};
+use std::{
+    fs::{create_dir_all, File},
+    io::BufReader,
+    path::PathBuf,
+    sync::Arc,
+};
 
 use async_executor::Executor;
 use async_trait::async_trait;
@@ -6,7 +11,7 @@ use chrono::{TimeZone, Utc};
 use clap::{IntoApp, Parser};
 use log::debug;
 use rand::{distributions::Alphanumeric, thread_rng, Rng};
-use serde::{Deserialize, Serialize};
+use serde::{de::DeserializeOwned, Deserialize, Serialize};
 use serde_json::{json, Value};
 use simplelog::{ColorChoice, TermLogger, TerminalMode};
 
@@ -49,7 +54,21 @@ fn find_free_id(tasks_ids: &Vec<u32>) -> u32 {
     1
 }
 
-#[derive(Clone, Debug, Serialize, Deserialize)]
+fn load<T: DeserializeOwned>(path: &PathBuf) -> Result<T> {
+    let file = File::open(path)?;
+    let reader = BufReader::new(file);
+
+    let value: T = serde_json::from_reader(reader)?;
+    Ok(value)
+}
+
+fn save<T: Serialize>(path: &PathBuf, value: &T) -> Result<()> {
+    let file = File::create(path)?;
+    serde_json::to_writer_pretty(file, value)?;
+    Ok(())
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
 struct Timestamp(String);
 
 #[derive(Clone, Debug, Serialize, Deserialize)]
@@ -67,13 +86,19 @@ struct Settings {
     dataset_path: PathBuf,
 }
 
-#[derive(Clone, Debug, Serialize, Deserialize)]
+impl Default for Settings {
+    fn default() -> Self {
+        Self { dataset_path: PathBuf::from("") }
+    }
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
 struct TaskEvent {
     action: String,
     timestamp: Timestamp,
 }
 
-#[derive(Clone, Debug, Serialize, Deserialize)]
+#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
 struct Comment {
     content: String,
     author: String,
@@ -83,11 +108,42 @@ struct Comment {
 #[derive(Clone, Debug, Serialize, Deserialize)]
 struct MonthTasks {
     created_at: Timestamp,
+    #[serde(skip_serializing, skip_deserializing)]
     settings: Settings,
-    task_tks: Vec<TaskInfo>,
+    task_tks: Vec<String>,
 }
 
-#[derive(Clone, Debug, Serialize, Deserialize)]
+impl MonthTasks {
+    fn add(&mut self, tk_hash: &str) {
+        self.task_tks.push(tk_hash.into());
+    }
+
+    fn objects(&self) -> Result<Vec<TaskInfo>> {
+        let mut tks: Vec<TaskInfo> = vec![];
+
+        for tk_hash in self.task_tks.iter() {
+            tks.push(TaskInfo::load(&tk_hash, &self.settings)?);
+        }
+
+        Ok(tks)
+    }
+
+    fn remove(&mut self, tk_hash: &str) {
+        if let Some(index) = self.task_tks.iter().position(|t| *t == tk_hash) {
+            self.task_tks.remove(index);
+        }
+    }
+
+    fn load(date: Timestamp, settings: Settings) -> Result<Timestamp> {
+        Ok(Timestamp(Utc::now().to_string()))
+    }
+
+    fn load_or_create(date: Timestamp, settings: Settings) -> Result<Timestamp> {
+        Ok(Timestamp(Utc::now().to_string()))
+    }
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
 struct TaskInfo {
     ref_id: String,
     id: u32,
@@ -103,7 +159,7 @@ struct TaskInfo {
 }
 
 impl TaskInfo {
-    pub fn new(title: String, desc: String, due: Option<Timestamp>, rank: u32) -> Self {
+    pub fn new(title: &str, desc: &str, due: Option<Timestamp>, rank: u32) -> Self {
         // TODO
         // check due date
 
@@ -119,8 +175,8 @@ impl TaskInfo {
         Self {
             ref_id,
             id,
-            title,
-            desc,
+            title: title.into(),
+            desc: desc.into(),
             assign: vec![],
             project: vec![],
             due,
@@ -139,7 +195,11 @@ impl TaskInfo {
         self.project.push(p);
     }
 
-    fn save(&self) -> Result<()> {
+    fn load(tk_hash: &str, settings: &Settings) -> Result<TaskInfo> {
+        Ok(TaskInfo::new("test", "test", None, 0))
+    }
+
+    fn save(&self, settings: &Settings) -> Result<()> {
         Ok(())
     }
 }
@@ -213,7 +273,7 @@ impl JsonRpcInterface {
                     None
                 };
 
-                task = TaskInfo::new(title.to_string(), desc.to_string(), due, rank as u32);
+                task = TaskInfo::new(title, desc, due, rank as u32);
             }
             (None, _, _) => {
                 return JsonResult::Err(jsonerr(InvalidParams, Some("invalid title".into()), id))
@@ -240,7 +300,7 @@ impl JsonRpcInterface {
             }
         }
 
-        match task.save() {
+        match task.save(&self.settings) {
             Ok(()) => JsonResult::Resp(jsonresp(json!(true), id)),
             Err(e) => JsonResult::Err(jsonerr(ServerError(-32603), Some(e.to_string()), id)),
         }
@@ -277,6 +337,12 @@ async fn main() -> Result<()> {
 mod tests {
     use super::*;
 
+    impl PartialEq for MonthTasks {
+        fn eq(&self, other: &Self) -> bool {
+            self.created_at == other.created_at && self.task_tks == other.task_tks
+        }
+    }
+
     #[test]
     fn find_free_id_test() -> Result<()> {
         let mut ids: Vec<u32> = vec![1, 3, 8, 9, 10, 3];
@@ -297,4 +363,57 @@ mod tests {
 
         Ok(())
     }
+
+    #[test]
+    fn load_and_save_data() -> Result<()> {
+        let path = PathBuf::from("/tmp/test_tau_data");
+
+        // mkdir dataset_path if not exists
+        create_dir_all(path.join("month"))?;
+        create_dir_all(path.join("task"))?;
+
+        // test with MonthTasks
+        ///////////////////////
+        let mt_path = path.join("month");
+        let mt_path = mt_path.join("022");
+
+        let settings = Settings { dataset_path: path.clone() };
+        let task_tks = vec![];
+        let created_at = Timestamp(Utc::now().to_string());
+
+        let mut mt = MonthTasks { created_at, task_tks, settings };
+
+        save::<MonthTasks>(&mt_path, &mt)?;
+
+        let mt_load = load::<MonthTasks>(&mt_path)?;
+        assert_eq!(mt, mt_load);
+
+        mt.add("test_hash");
+
+        save::<MonthTasks>(&mt_path, &mt)?;
+
+        let mt_load = load::<MonthTasks>(&mt_path)?;
+        assert_eq!(mt, mt_load);
+
+        // test with TaskInfo
+        ///////////////////////
+        let t_path = path.join("task");
+        let t_path = t_path.join("test_hash");
+
+        let mut task = TaskInfo::new("test_title", "test_desc", None, 0);
+
+        save::<TaskInfo>(&t_path, &task)?;
+
+        let t_load = load::<TaskInfo>(&t_path)?;
+        assert_eq!(task, t_load);
+
+        task.title = "test_title_2".into();
+
+        save::<TaskInfo>(&t_path, &task)?;
+
+        let t_load = load::<TaskInfo>(&t_path)?;
+        assert_eq!(task, t_load);
+
+        Ok(())
+    }
 }