Prechádzať zdrojové kódy

bin/darkwikid: support documents in directories

ghassmo 4 rokov pred
rodič
commit
4a036df248
2 zmenil súbory, kde vykonal 76 pridanie a 42 odobranie
  1. 71 37
      bin/darkwikid/src/main.rs
  2. 5 5
      bin/darkwikid/src/patch.rs

+ 71 - 37
bin/darkwikid/src/main.rs

@@ -1,7 +1,7 @@
 use async_std::sync::{Arc, Mutex};
 use std::{
     fs::{create_dir_all, read_dir},
-    path::PathBuf,
+    path::{Path, PathBuf},
 };
 
 use async_executor::Executor;
@@ -27,14 +27,13 @@ use darkfi::{
         file::{load_file, load_json_file, save_file, save_json_file},
         path::get_config_path,
     },
-    Error, Result,
+    Result,
 };
 
 mod error;
 mod jsonrpc;
 mod patch;
 
-use error::DarkWikiResult;
 use jsonrpc::JsonRpcInterface;
 use patch::{OpMethod, Patch};
 
@@ -114,12 +113,32 @@ fn lcs(a: &str, b: &str) -> Vec<OpMethod> {
     result
 }
 
-fn title_to_id(title: &str) -> String {
+fn path_to_id(path: &str) -> String {
     let mut hasher = sha2::Sha256::new();
-    hasher.update(title);
+    hasher.update(path);
     hex::encode(hasher.finalize())
 }
 
+fn get_docs_paths(files: &mut Vec<PathBuf>, path: &Path, parent: Option<&Path>) -> Result<()> {
+    let docs = read_dir(&path)?;
+    let docs = docs.filter(|d| d.is_ok()).map(|d| d.unwrap().path()).collect::<Vec<PathBuf>>();
+
+    for doc in docs {
+        if let Some(file_name) = doc.file_name() {
+            let file_name = PathBuf::from(file_name);
+            let file_name =
+                if let Some(parent) = parent { parent.join(file_name) } else { file_name };
+            if doc.is_file() {
+                files.push(file_name);
+            } else if doc.is_dir() {
+                get_docs_paths(files, &doc, Some(&file_name))?;
+            }
+        }
+    }
+
+    Ok(())
+}
+
 struct Darkwiki {
     settings: DarkWikiSettings,
     rpc: (
@@ -130,11 +149,11 @@ struct Darkwiki {
 }
 
 impl Darkwiki {
-    async fn start(&self) -> DarkWikiResult<()> {
+    async fn start(&self) -> Result<()> {
         loop {
             select! {
                 val = self.rpc.1.recv().fuse() => {
-                    let (cmd, dry, files) = val.map_err(Error::from)?;
+                    let (cmd, dry, files) = val?;
                     match cmd.as_str() {
                         "update" => {
                             self.on_receive_update(dry, files).await?;
@@ -146,7 +165,7 @@ impl Darkwiki {
                     }
                 }
                 patch = self.raft.1.recv().fuse() => {
-                    let patch = patch.map_err(Error::from)?;
+                    let patch = patch?;
                     info!("Receive new patch from Raft {:?}", patch);
                     self.on_receive_patch(&patch)?;
                 }
@@ -155,7 +174,7 @@ impl Darkwiki {
         }
     }
 
-    fn on_receive_patch(&self, received_patch: &Patch) -> DarkWikiResult<()> {
+    fn on_receive_patch(&self, received_patch: &Patch) -> Result<()> {
         let sync_id_path = self.settings.datastore_path.join("sync").join(&received_patch.id);
         let local_id_path = self.settings.datastore_path.join("local").join(&received_patch.id);
 
@@ -185,65 +204,65 @@ impl Darkwiki {
         Ok(())
     }
 
-    async fn on_receive_update(&self, dry: bool, files: Vec<String>) -> DarkWikiResult<()> {
+    async fn on_receive_update(&self, dry: bool, files: Vec<String>) -> Result<()> {
         let (patches, local, sync, merge) = self.update(dry, files)?;
 
         if !dry {
             for patch in patches {
                 info!("Send a patch to Raft {:?}", patch);
-                self.raft.0.send(patch.clone()).await.map_err(Error::from)?;
+                self.raft.0.send(patch.clone()).await?;
             }
         }
 
         let local: Vec<(String, String)> =
-            local.iter().map(|p| (p.title.to_owned(), p.colorize())).collect();
+            local.iter().map(|p| (p.path.to_owned(), p.colorize())).collect();
 
         let sync: Vec<(String, String)> =
-            sync.iter().map(|p| (p.title.to_owned(), p.colorize())).collect();
+            sync.iter().map(|p| (p.path.to_owned(), p.colorize())).collect();
 
         let merge: Vec<(String, String)> =
-            merge.iter().map(|p| (p.title.to_owned(), p.colorize())).collect();
+            merge.iter().map(|p| (p.path.to_owned(), p.colorize())).collect();
 
-        self.rpc.0.send(vec![local, sync, merge]).await.map_err(Error::from)?;
+        self.rpc.0.send(vec![local, sync, merge]).await?;
 
         Ok(())
     }
 
-    async fn on_receive_restore(&self, dry: bool, files_name: Vec<String>) -> DarkWikiResult<()> {
+    async fn on_receive_restore(&self, dry: bool, files_name: Vec<String>) -> Result<()> {
         let patches = self.restore(dry, files_name)?;
         let patches: Vec<(String, String)> =
-            patches.iter().map(|p| (p.title.to_owned(), p.to_string())).collect();
+            patches.iter().map(|p| (p.path.to_owned(), p.to_string())).collect();
 
-        self.rpc.0.send(vec![patches]).await.map_err(Error::from)?;
+        self.rpc.0.send(vec![patches]).await?;
 
         Ok(())
     }
 
-    fn restore(&self, dry: bool, files_name: Vec<String>) -> DarkWikiResult<Vec<Patch>> {
+    fn restore(&self, dry: bool, files_name: Vec<String>) -> Result<Vec<Patch>> {
         let local_path = self.settings.datastore_path.join("local");
         let docs_path = self.settings.docs_path.clone();
-        let local_files = read_dir(&local_path).map_err(Error::from)?;
 
         let mut patches = vec![];
 
+        let local_files = read_dir(&local_path)?;
         for file in local_files {
-            let file_id = file.as_ref().unwrap().file_name();
+            let file_id = file?.file_name();
             let file_id = file_id.to_str().unwrap();
             let file_path = local_path.join(&file_id);
             let local_patch: Patch = load_json_file(&file_path)?;
 
-            if !files_name.is_empty() && !files_name.contains(&local_patch.title.to_string()) {
+            if !files_name.is_empty() && !files_name.contains(&local_patch.path.to_string()) {
                 continue
             }
 
-            if let Ok(doc) = load_file(&docs_path.join(&local_patch.title)) {
+            if let Ok(doc) = load_file(&docs_path.join(&local_patch.path)) {
                 if local_patch.to_string() == doc {
                     continue
                 }
             }
 
             if !dry {
-                save_file(&docs_path.join(&local_patch.title), &local_patch.to_string())?;
+                self.save_doc(&local_patch.path, &local_patch.to_string())?;
             }
 
             patches.push(local_patch);
@@ -252,7 +271,7 @@ impl Darkwiki {
         Ok(patches)
     }
 
-    fn update(&self, dry: bool, files_name: Vec<String>) -> DarkWikiResult<Patches> {
+    fn update(&self, dry: bool, files_name: Vec<String>) -> Result<Patches> {
         let mut patches: Vec<Patch> = vec![];
         let mut local_patches: Vec<Patch> = vec![];
         let mut sync_patches: Vec<Patch> = vec![];
@@ -264,23 +283,28 @@ impl Darkwiki {
 
         // save and compare docs in darkwiki and local dirs
         // then merged with sync patches if any received
-        let docs = read_dir(&docs_path).map_err(Error::from)?;
+        let mut docs = vec![];
+        get_docs_paths(&mut docs, &docs_path, None)?;
         for doc in docs {
-            let doc_title = doc.as_ref().unwrap().file_name();
-            let doc_title = doc_title.to_str().unwrap();
+            let doc_path = doc.to_str().unwrap();
 
-            if !files_name.is_empty() && !files_name.contains(&doc_title.to_string()) {
+            if !files_name.is_empty() && !files_name.contains(&doc_path.to_string()) {
                 continue
             }
 
             // load doc content
-            let edit = load_file(&docs_path.join(doc_title)).map_err(Error::from)?;
+            let edit = load_file(&docs_path.join(doc_path))?;
+
+            if edit.is_empty() {
+                continue
+            }
+
             let edit = edit.trim();
 
-            let doc_id = title_to_id(doc_title);
+            let doc_id = path_to_id(doc_path);
 
             // create new patch
-            let mut new_patch = Patch::new(doc_title, &doc_id, &self.settings.author);
+            let mut new_patch = Patch::new(doc_path, &doc_id, &self.settings.author);
 
             // check for any changes found with local doc and darkwiki doc
             if let Ok(local_patch) = load_json_file::<Patch>(&local_path.join(&doc_id)) {
@@ -313,7 +337,7 @@ impl Darkwiki {
                         let sync_patch_t = new_patch.transform(&sync_patch);
                         new_patch = new_patch.merge(&sync_patch_t);
                         if !dry {
-                            save_file(&docs_path.join(doc_title), &new_patch.to_string())?;
+                            self.save_doc(doc_path, &new_patch.to_string())?;
                         }
                         merge_patches.push(new_patch.clone());
                     }
@@ -332,9 +356,9 @@ impl Darkwiki {
 
         // check if a new patch received
         // and save the new changes in both local and darkwiki dirs
-        let sync_files = read_dir(&sync_path).map_err(Error::from)?;
+        let sync_files = read_dir(&sync_path)?;
         for file in sync_files {
-            let file_id = file.as_ref().unwrap().file_name();
+            let file_id = file?.file_name();
             let file_id = file_id.to_str().unwrap();
             let file_path = sync_path.join(&file_id);
             let sync_patch: Patch = load_json_file(&file_path)?;
@@ -345,12 +369,12 @@ impl Darkwiki {
                 }
             }
 
-            if !files_name.is_empty() && !files_name.contains(&sync_patch.title.to_string()) {
+            if !files_name.is_empty() && !files_name.contains(&sync_patch.path.to_string()) {
                 continue
             }
 
             if !dry {
-                save_file(&docs_path.join(&sync_patch.title), &sync_patch.to_string())?;
+                self.save_doc(&sync_patch.path, &sync_patch.to_string())?;
                 save_json_file(&local_path.join(file_id), &sync_patch)?;
             }
 
@@ -361,6 +385,16 @@ impl Darkwiki {
 
         Ok((patches, local_patches, sync_patches, merge_patches))
     }
+
+    fn save_doc(&self, path: &str, edit: &str) -> Result<()> {
+        let path = self.settings.docs_path.join(path);
+        if let Some(p) = path.parent() {
+            if !p.exists() && !p.to_str().unwrap().is_empty() {
+                create_dir_all(p)?;
+            }
+        }
+        save_file(&path, edit)
+    }
 }
 
 async_daemonize!(realmain);

+ 5 - 5
bin/darkwikid/src/patch.rs

@@ -22,7 +22,7 @@ pub struct OpMethods(pub Vec<OpMethod>);
 
 #[derive(PartialEq, Eq, SerialEncodable, SerialDecodable, Serialize, Deserialize, Clone, Debug)]
 pub struct Patch {
-    pub title: String,
+    pub path: String,
     pub author: String,
     pub id: String,
     pub base: String,
@@ -66,9 +66,9 @@ impl std::string::ToString for Patch {
 }
 
 impl Patch {
-    pub fn new(title: &str, id: &str, author: &str) -> Self {
+    pub fn new(path: &str, id: &str, author: &str) -> Self {
         Self {
-            title: title.to_string(),
+            path: path.to_string(),
             id: id.to_string(),
             ops: OpMethods(vec![]),
             base: String::new(),
@@ -146,7 +146,7 @@ impl Patch {
     //
     // TODO need more work to get better performance with iterators
     pub fn transform(&self, other: &Self) -> Self {
-        let mut new_patch = Self::new(&self.title, &self.id, &self.author);
+        let mut new_patch = Self::new(&self.path, &self.id, &self.author);
         new_patch.base = self.base.clone();
 
         let mut ops1 = self.ops.0.iter().cloned();
@@ -253,7 +253,7 @@ impl Patch {
         let mut ops1 = ops1.iter().cloned();
         let mut ops2 = other.ops.0.iter().cloned();
 
-        let mut new_patch = Self::new(&self.title, &self.id, &self.author);
+        let mut new_patch = Self::new(&self.path, &self.id, &self.author);
         new_patch.base = self.base.clone();
 
         let mut op1 = ops1.next();