diff --git a/crates/crdb/src/crdb.rs b/crates/crdb/src/crdb.rs index 56b0c86158..2822f626b7 100644 --- a/crates/crdb/src/crdb.rs +++ b/crates/crdb/src/crdb.rs @@ -15,7 +15,7 @@ use serde::{Deserialize, Serialize}; use smallvec::{smallvec, SmallVec}; use std::{ cmp::{self, Ordering}, - fmt::Debug, + fmt::{self, Debug, Display}, future::Future, ops::Range, path::Path, @@ -29,14 +29,20 @@ use uuid::Uuid; )] pub struct RepoId(Uuid); -type RevisionId = SmallVec<[OperationId; 2]>; - impl RepoId { fn new() -> Self { RepoId(Uuid::new_v4()) } } +impl Display for RepoId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0.as_hyphenated()) + } +} + +type RevisionId = SmallVec<[OperationId; 2]>; + #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct ReplicaId(u32); @@ -78,6 +84,11 @@ pub struct RoomName(Arc); #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize)] pub struct RoomToken(Arc); +#[derive(Clone)] +pub struct User { + login: Arc, +} + pub trait Request: Message { type Response: Message; } @@ -105,9 +116,12 @@ where pub trait ServerNetwork: 'static + Send + Sync { fn on_request(&self, handle_request: H) where - H: 'static + Send + Sync + Fn(R) -> F, - F: 'static + Send + Sync + futures::Future>, + H: 'static + Send + Fn(User, R) -> F, + F: 'static + Send + futures::Future>, R: Request; + + fn create_room(&self, room: &RoomName) -> BoxFuture>; + fn grant_room_access(&self, room: &RoomName, user: &str) -> RoomToken; } pub trait ClientNetwork: 'static + Send + Sync { @@ -212,49 +226,67 @@ impl Client { fn handle_remote_operation(&self, repo_id: RepoId, operation: Operation) {} } -#[derive(Clone)] -struct Server { +struct Server { db: Db, + network: Arc, repo_ids_by_name: Arc, RepoId>>>, } -impl Server { - fn new(network: impl ServerNetwork) -> Self { +impl Clone for Server { + fn clone(&self) -> Self { + Self { + db: self.db.clone(), + network: self.network.clone(), + repo_ids_by_name: Default::default(), + } + } +} + +impl Server { + fn new(network: N) -> Self { let this = Self { db: Db::new(), + network: Arc::new(network), repo_ids_by_name: Default::default(), }; - this.on_request(network, Self::handle_publish_repo); + this.on_request(Self::handle_publish_repo); this } - fn on_request(&self, network: S, handle_request: F) + fn on_request(&self, handle_request: F) where - F: 'static + Send + Sync + Fn(Self, R) -> Fut, - Fut: 'static + Send + Sync + Future>, + F: 'static + Send + Fn(Self, User, R) -> Fut, + Fut: 'static + Send + Future>, R: Request, - S: ServerNetwork, { - network.on_request({ + self.network.on_request({ let this = self.clone(); - move |request| handle_request(this.clone(), request) + move |user, request| handle_request(this.clone(), user, request) }); } async fn handle_publish_repo( self, + user: User, request: messages::PublishRepo, ) -> Result { // TODO: handle repositories that had already been published. - match self.repo_ids_by_name.lock().entry(request.name) { - hash_map::Entry::Occupied(_) => Err(anyhow!("repo name taken")), + match self.repo_ids_by_name.lock().entry(request.name.clone()) { + hash_map::Entry::Occupied(_) => return Err(anyhow!("repo name taken")), hash_map::Entry::Vacant(entry) => { let mut db = self.db.snapshot.lock(); db.repos.insert(request.id, Default::default()); entry.insert(request.id); - todo!() } } + + let room = RoomName(request.id.to_string().into()); + self.network.create_room(&room).await?; + let token = self.network.grant_room_access(&room, user.login.as_ref()); + + Ok(messages::PublishRepoResponse { + credentials: RoomCredentials { name: room, token }, + }) } } @@ -941,17 +973,13 @@ struct RepoSnapshot { branches: TreeMap, operations: TreeMap, revisions: TreeMap, - name: Option>, } impl RepoSnapshot { fn new(replica_id: ReplicaId) -> Self { Self { last_operation_id: OperationId::new(replica_id), - branches: Default::default(), - operations: Default::default(), - revisions: Default::default(), - name: None, + ..Default::default() } } @@ -1003,7 +1031,7 @@ mod tests { let network = TestNetwork::new(deterministic.build_background()); let server = Server::new(network.server()); - let client_a = Client::new(network.client()); + let client_a = Client::new(network.client("client-a")); let repo_a = client_a.create_repo(); let branch_a = repo_a.create_empty_branch("main"); @@ -1017,7 +1045,7 @@ mod tests { assert_eq!(doc2.text().to_string(), "def"); client_a.publish_repo(&repo_a, "repo-1").await.unwrap(); - let db_b = Client::new(network.client()); + let db_b = Client::new(network.client("client-b")); let repo_b = db_b.clone_repo("repo-1").await.unwrap(); } } diff --git a/crates/crdb/src/messages.rs b/crates/crdb/src/messages.rs index 2fef87ada8..80e1146bfb 100644 --- a/crates/crdb/src/messages.rs +++ b/crates/crdb/src/messages.rs @@ -1,4 +1,4 @@ -use crate::{Message, RepoId, Request, RoomCredentials}; +use crate::{RepoId, Request, RoomCredentials}; use serde::{Deserialize, Serialize}; use std::sync::Arc; diff --git a/crates/crdb/src/operations.rs b/crates/crdb/src/operations.rs index 8784294c7b..56d57805cc 100644 --- a/crates/crdb/src/operations.rs +++ b/crates/crdb/src/operations.rs @@ -1,4 +1,4 @@ -use crate::{AnchorRange, Message, OperationId, RevisionId}; +use crate::{AnchorRange, OperationId, RevisionId}; use serde::{Deserialize, Serialize}; use smallvec::SmallVec; use std::sync::Arc; diff --git a/crates/crdb/src/test.rs b/crates/crdb/src/test.rs index a6df1e6dd9..1382084c1a 100644 --- a/crates/crdb/src/test.rs +++ b/crates/crdb/src/test.rs @@ -1,5 +1,5 @@ use crate::{ - ClientNetwork, ClientRoom, Message, RoomCredentials, RoomName, RoomToken, ServerNetwork, + ClientNetwork, ClientRoom, Message, RoomCredentials, RoomName, RoomToken, ServerNetwork, User, }; use anyhow::Result; use collections::HashMap; @@ -27,8 +27,13 @@ impl TestNetwork { TestServer(self.0.clone()) } - pub fn client(&self) -> TestClient { - TestClient(self.0.clone()) + pub fn client(&self, login: impl Into>) -> TestClient { + TestClient { + user: User { + login: login.into(), + }, + network: self.0.clone(), + } } } @@ -36,7 +41,7 @@ struct NetworkState { executor: Arc, request_handlers: BTreeMap< TypeId, - Box) -> BoxFuture<'static, Result>>>, + Box) -> BoxFuture<'static, Result>>>, >, rooms: BTreeMap, } @@ -50,15 +55,15 @@ pub struct TestServer(Arc>); impl ServerNetwork for TestServer { fn on_request(&self, handle_request: H) where - H: 'static + Send + Sync + Fn(R) -> F, - F: 'static + Send + Sync + futures::Future>, + H: 'static + Send + Fn(User, R) -> F, + F: 'static + Send + futures::Future>, R: crate::Request, { self.0.lock().request_handlers.insert( TypeId::of::(), - Box::new(move |request| { + Box::new(move |user, request| { let request = request.downcast::().unwrap(); - let response = handle_request(*request); + let response = handle_request(user, *request); async move { response .await @@ -68,9 +73,20 @@ impl ServerNetwork for TestServer { }), ); } + + fn create_room(&self, room: &RoomName) -> BoxFuture> { + todo!() + } + + fn grant_room_access(&self, room: &RoomName, user: &str) -> RoomToken { + todo!() + } } -pub struct TestClient(Arc>); +pub struct TestClient { + user: User, + network: Arc>, +} impl ClientNetwork for TestClient { type Room = TestClientRoom; @@ -79,7 +95,7 @@ impl ClientNetwork for TestClient { &self, request: R, ) -> futures::future::BoxFuture> { - let network = self.0.lock(); + let network = self.network.lock(); let executor = network.executor.clone(); let request = network .request_handlers @@ -87,7 +103,7 @@ impl ClientNetwork for TestClient { .expect(&format!( "handler for request {} not found", type_name::() - ))(Box::new(request)); + ))(self.user.clone(), Box::new(request)); async move { executor.simulate_random_delay().await; let response = request @@ -104,7 +120,7 @@ impl ClientNetwork for TestClient { outbox: Default::default(), credentials, message_handlers: Default::default(), - network: self.0.clone(), + network: self.network.clone(), } } }