Move persistence and restoration logic from workspace into project

Max Brunsfeld and Antonio Scandurra created

Co-authored-by: Antonio Scandurra <me@as-cii.com>

Change summary

crates/project/src/project.rs     | 136 ++++++++++++++++++--------------
crates/workspace/src/workspace.rs |  23 ----
2 files changed, 81 insertions(+), 78 deletions(-)

Detailed changes

crates/project/src/project.rs 🔗

@@ -552,6 +552,50 @@ impl Project {
         project
     }
 
+    pub fn restore_state(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+        if self.is_remote() {
+            return Task::ready(Ok(()));
+        }
+
+        let db = self.project_store.read(cx).db.clone();
+        let project_path_keys = self.project_path_keys(cx);
+        let should_be_public = cx.background().spawn(async move {
+            let values = db.read(project_path_keys)?;
+            anyhow::Ok(values.into_iter().all(|e| e.is_some()))
+        });
+        cx.spawn(|this, mut cx| async move {
+            let public = should_be_public.await.log_err().unwrap_or(false);
+            this.update(&mut cx, |this, cx| {
+                if let ProjectClientState::Local { public_tx, .. } = &mut this.client_state {
+                    let mut public_tx = public_tx.borrow_mut();
+                    if *public_tx != public {
+                        *public_tx = public;
+                        drop(public_tx);
+                        this.metadata_changed(false, cx);
+                    }
+                }
+            });
+            Ok(())
+        })
+    }
+
+    fn persist_state(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
+        if self.is_remote() {
+            return Task::ready(Ok(()));
+        }
+
+        let db = self.project_store.read(cx).db.clone();
+        let project_path_keys = self.project_path_keys(cx);
+        let public = self.is_public();
+        cx.background().spawn(async move {
+            if public {
+                db.write(project_path_keys.into_iter().map(|key| (key, &[])))
+            } else {
+                db.delete(project_path_keys)
+            }
+        })
+    }
+
     pub fn buffer_for_id(&self, remote_id: u64, cx: &AppContext) -> Option<ModelHandle<Buffer>> {
         self.opened_buffers
             .get(&remote_id)
@@ -633,8 +677,12 @@ impl Project {
 
     pub fn set_public(&mut self, is_public: bool, cx: &mut ModelContext<Self>) {
         if let ProjectClientState::Local { public_tx, .. } = &mut self.client_state {
-            *public_tx.borrow_mut() = is_public;
-            self.metadata_changed(cx);
+            let mut public_tx = public_tx.borrow_mut();
+            if *public_tx != is_public {
+                *public_tx = is_public;
+                drop(public_tx);
+                self.metadata_changed(true, cx);
+            }
         }
     }
 
@@ -674,7 +722,7 @@ impl Project {
                             *remote_id_tx.borrow_mut() = None;
                         }
                         this.subscriptions.clear();
-                        this.metadata_changed(cx);
+                        this.metadata_changed(false, cx);
                     });
                     response.map(drop)
                 });
@@ -698,7 +746,7 @@ impl Project {
                     *remote_id_tx.borrow_mut() = Some(remote_id);
                 }
 
-                this.metadata_changed(cx);
+                this.metadata_changed(false, cx);
                 cx.emit(Event::RemoteIdChanged(Some(remote_id)));
                 this.subscriptions
                     .push(this.client.add_model_for_remote_entity(remote_id, cx));
@@ -761,7 +809,7 @@ impl Project {
         }
     }
 
-    fn metadata_changed(&mut self, cx: &mut ModelContext<Self>) {
+    fn metadata_changed(&mut self, persist: bool, cx: &mut ModelContext<Self>) {
         if let ProjectClientState::Local {
             remote_id_rx,
             public_rx,
@@ -786,6 +834,9 @@ impl Project {
             }
 
             self.project_store.update(cx, |_, cx| cx.notify());
+            if persist {
+                self.persist_state(cx).detach_and_log_err(cx);
+            }
             cx.notify();
         }
     }
@@ -823,6 +874,25 @@ impl Project {
             .map(|tree| tree.read(cx).root_name())
     }
 
+    fn project_path_keys(&self, cx: &AppContext) -> Vec<String> {
+        self.worktrees
+            .iter()
+            .filter_map(|worktree| {
+                worktree.upgrade(&cx).map(|worktree| {
+                    format!(
+                        "public-project-path:{}",
+                        worktree
+                            .read(cx)
+                            .as_local()
+                            .unwrap()
+                            .abs_path()
+                            .to_string_lossy()
+                    )
+                })
+            })
+            .collect::<Vec<_>>()
+    }
+
     pub fn worktree_for_id(
         &self,
         id: WorktreeId,
@@ -3769,7 +3839,7 @@ impl Project {
                 false
             }
         });
-        self.metadata_changed(cx);
+        self.metadata_changed(true, cx);
         cx.notify();
     }
 
@@ -3799,7 +3869,7 @@ impl Project {
             self.worktrees
                 .push(WorktreeHandle::Weak(worktree.downgrade()));
         }
-        self.metadata_changed(cx);
+        self.metadata_changed(true, cx);
         cx.emit(Event::WorktreeAdded);
         cx.notify();
     }
@@ -4122,7 +4192,7 @@ impl Project {
                 }
             }
 
-            this.metadata_changed(cx);
+            this.metadata_changed(true, cx);
             for (id, _) in old_worktrees_by_id {
                 cx.emit(Event::WorktreeRemoved(id));
             }
@@ -5274,56 +5344,6 @@ impl ProjectStore {
             cx.notify();
         }
     }
-
-    pub fn are_all_project_paths_public(
-        &self,
-        project: &Project,
-        cx: &AppContext,
-    ) -> Task<Result<bool>> {
-        let project_path_keys = self.project_path_keys(project, cx);
-        let db = self.db.clone();
-        cx.background().spawn(async move {
-            let values = db.read(project_path_keys)?;
-            Ok(values.into_iter().all(|e| e.is_some()))
-        })
-    }
-
-    pub fn set_project_paths_public(
-        &self,
-        project: &Project,
-        public: bool,
-        cx: &AppContext,
-    ) -> Task<Result<()>> {
-        let project_path_keys = self.project_path_keys(project, cx);
-        let db = self.db.clone();
-        cx.background().spawn(async move {
-            if public {
-                db.write(project_path_keys.into_iter().map(|key| (key, &[])))
-            } else {
-                db.delete(project_path_keys)
-            }
-        })
-    }
-
-    fn project_path_keys(&self, project: &Project, cx: &AppContext) -> Vec<String> {
-        project
-            .worktrees
-            .iter()
-            .filter_map(|worktree| {
-                worktree.upgrade(&cx).map(|worktree| {
-                    format!(
-                        "public-project-path:{}",
-                        worktree
-                            .read(cx)
-                            .as_local()
-                            .unwrap()
-                            .abs_path()
-                            .to_string_lossy()
-                    )
-                })
-            })
-            .collect::<Vec<_>>()
-    }
 }
 
 impl WorktreeHandle {

crates/workspace/src/workspace.rs 🔗

@@ -1057,12 +1057,6 @@ impl Workspace {
         project.update(cx, |project, cx| {
             let public = !project.is_public();
             project.set_public(public, cx);
-            project.project_store().update(cx, |store, cx| {
-                store
-                    .set_project_paths_public(project, public, cx)
-                    .detach_and_log_err(cx);
-                cx.notify();
-            });
         });
     }
 
@@ -2454,21 +2448,10 @@ pub fn open_paths(
             .await;
 
         if let Some(project) = new_project {
-            let public = project
-                .read_with(&cx, |project, cx| {
-                    app_state
-                        .project_store
-                        .read(cx)
-                        .are_all_project_paths_public(project, cx)
-                })
+            project
+                .update(&mut cx, |project, cx| project.restore_state(cx))
                 .await
-                .log_err()
-                .unwrap_or(false);
-            if public {
-                project.update(&mut cx, |project, cx| {
-                    project.set_public(true, cx);
-                });
-            }
+                .log_err();
         }
 
         (workspace, items)