Move leave_project from Store to db module

Nathan Sobo created

Change summary

crates/collab/src/db.rs        | 70 ++++++++++++++++++++++++++++++++++-
crates/collab/src/rpc.rs       | 27 +++++++------
crates/collab/src/rpc/store.rs | 31 ---------------
crates/rpc/src/peer.rs         |  2 
4 files changed, 82 insertions(+), 48 deletions(-)

Detailed changes

crates/collab/src/db.rs 🔗

@@ -1209,6 +1209,7 @@ where
                                     id: collaborator.project_id,
                                     host_user_id: Default::default(),
                                     connection_ids: Default::default(),
+                                    host_connection_id: Default::default(),
                                 });
 
                         let collaborator_connection_id =
@@ -1219,6 +1220,8 @@ where
 
                         if collaborator.is_host {
                             left_project.host_user_id = collaborator.user_id;
+                            left_project.host_connection_id =
+                                ConnectionId(collaborator.connection_id as u32);
                         }
                     }
                 }
@@ -1474,7 +1477,8 @@ where
             .bind(user_id)
             .bind(connection_id.0 as i32)
             .fetch_one(&mut tx)
-            .await?;
+            .await
+            .unwrap();
 
             if !worktrees.is_empty() {
                 let mut params = "(?, ?, ?, ?, ?, ?, ?),".repeat(worktrees.len());
@@ -1505,7 +1509,7 @@ where
                         .bind(0)
                         .bind(false);
                 }
-                query.execute(&mut tx).await?;
+                query.execute(&mut tx).await.unwrap();
             }
 
             sqlx::query(
@@ -1526,7 +1530,8 @@ where
             .bind(0)
             .bind(true)
             .execute(&mut tx)
-            .await?;
+            .await
+            .unwrap();
 
             let room = self.commit_room_transaction(room_id, tx).await?;
             Ok((project_id, room))
@@ -2086,6 +2091,64 @@ where
         .await
     }
 
+    pub async fn leave_project(
+        &self,
+        project_id: ProjectId,
+        connection_id: ConnectionId,
+    ) -> Result<LeftProject> {
+        self.transact(|mut tx| async move {
+            let result = sqlx::query(
+                "
+                DELETE FROM project_collaborators
+                WHERE project_id = $1 AND connection_id = $2
+                ",
+            )
+            .bind(project_id)
+            .bind(connection_id.0 as i32)
+            .execute(&mut tx)
+            .await?;
+
+            if result.rows_affected() != 1 {
+                Err(anyhow!("not a collaborator on this project"))?;
+            }
+
+            let connection_ids = sqlx::query_scalar::<_, i32>(
+                "
+                SELECT connection_id
+                FROM project_collaborators
+                WHERE project_id = $1
+                ",
+            )
+            .bind(project_id)
+            .fetch_all(&mut tx)
+            .await?
+            .into_iter()
+            .map(|id| ConnectionId(id as u32))
+            .collect();
+
+            let (host_user_id, host_connection_id) = sqlx::query_as::<_, (i32, i32)>(
+                "
+                SELECT host_user_id, host_connection_id
+                FROM projects
+                WHERE id = $1
+                ",
+            )
+            .bind(project_id)
+            .fetch_one(&mut tx)
+            .await?;
+
+            tx.commit().await?;
+
+            Ok(LeftProject {
+                id: project_id,
+                host_user_id: UserId(host_user_id),
+                host_connection_id: ConnectionId(host_connection_id as u32),
+                connection_ids,
+            })
+        })
+        .await
+    }
+
     pub async fn project_collaborators(
         &self,
         project_id: ProjectId,
@@ -2645,6 +2708,7 @@ struct LanguageServer {
 pub struct LeftProject {
     pub id: ProjectId,
     pub host_user_id: UserId,
+    pub host_connection_id: ConnectionId,
     pub connection_ids: Vec<ConnectionId>,
 }
 

crates/collab/src/rpc.rs 🔗

@@ -1041,8 +1041,11 @@ impl Server {
         let project_id = ProjectId::from_proto(request.payload.project_id);
         let project;
         {
-            let mut store = self.store().await;
-            project = store.leave_project(project_id, sender_id)?;
+            project = self
+                .app_state
+                .db
+                .leave_project(project_id, sender_id)
+                .await?;
             tracing::info!(
                 %project_id,
                 host_user_id = %project.host_user_id,
@@ -1050,17 +1053,15 @@ impl Server {
                 "leave project"
             );
 
-            if project.remove_collaborator {
-                broadcast(sender_id, project.connection_ids, |conn_id| {
-                    self.peer.send(
-                        conn_id,
-                        proto::RemoveProjectCollaborator {
-                            project_id: project_id.to_proto(),
-                            peer_id: sender_id.0,
-                        },
-                    )
-                });
-            }
+            broadcast(sender_id, project.connection_ids, |conn_id| {
+                self.peer.send(
+                    conn_id,
+                    proto::RemoveProjectCollaborator {
+                        project_id: project_id.to_proto(),
+                        peer_id: sender_id.0,
+                    },
+                )
+            });
         }
 
         Ok(())

crates/collab/src/rpc/store.rs 🔗

@@ -251,37 +251,6 @@ impl Store {
         }
     }
 
-    pub fn leave_project(
-        &mut self,
-        project_id: ProjectId,
-        connection_id: ConnectionId,
-    ) -> Result<LeftProject> {
-        let project = self
-            .projects
-            .get_mut(&project_id)
-            .ok_or_else(|| anyhow!("no such project"))?;
-
-        // If the connection leaving the project is a collaborator, remove it.
-        let remove_collaborator = if let Some(guest) = project.guests.remove(&connection_id) {
-            project.active_replica_ids.remove(&guest.replica_id);
-            true
-        } else {
-            false
-        };
-
-        if let Some(connection) = self.connections.get_mut(&connection_id) {
-            connection.projects.remove(&project_id);
-        }
-
-        Ok(LeftProject {
-            id: project.id,
-            host_connection_id: project.host_connection_id,
-            host_user_id: project.host.user_id,
-            connection_ids: project.connection_ids(),
-            remove_collaborator,
-        })
-    }
-
     #[cfg(test)]
     pub fn check_invariants(&self) {
         for (connection_id, connection) in &self.connections {

crates/rpc/src/peer.rs 🔗

@@ -24,7 +24,7 @@ use std::{
 };
 use tracing::instrument;
 
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)]
+#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)]
 pub struct ConnectionId(pub u32);
 
 impl fmt::Display for ConnectionId {