WIP
This commit is contained in:
parent
00b0189660
commit
be7d4d6ea9
4 changed files with 84 additions and 40 deletions
|
@ -15,7 +15,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use std::{
|
use std::{
|
||||||
cmp::{self, Ordering},
|
cmp::{self, Ordering},
|
||||||
fmt::Debug,
|
fmt::{self, Debug, Display},
|
||||||
future::Future,
|
future::Future,
|
||||||
ops::Range,
|
ops::Range,
|
||||||
path::Path,
|
path::Path,
|
||||||
|
@ -29,14 +29,20 @@ use uuid::Uuid;
|
||||||
)]
|
)]
|
||||||
pub struct RepoId(Uuid);
|
pub struct RepoId(Uuid);
|
||||||
|
|
||||||
type RevisionId = SmallVec<[OperationId; 2]>;
|
|
||||||
|
|
||||||
impl RepoId {
|
impl RepoId {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
RepoId(Uuid::new_v4())
|
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)]
|
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub struct ReplicaId(u32);
|
pub struct ReplicaId(u32);
|
||||||
|
|
||||||
|
@ -78,6 +84,11 @@ pub struct RoomName(Arc<str>);
|
||||||
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct RoomToken(Arc<str>);
|
pub struct RoomToken(Arc<str>);
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct User {
|
||||||
|
login: Arc<str>,
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Request: Message {
|
pub trait Request: Message {
|
||||||
type Response: Message;
|
type Response: Message;
|
||||||
}
|
}
|
||||||
|
@ -105,9 +116,12 @@ where
|
||||||
pub trait ServerNetwork: 'static + Send + Sync {
|
pub trait ServerNetwork: 'static + Send + Sync {
|
||||||
fn on_request<H, F, R>(&self, handle_request: H)
|
fn on_request<H, F, R>(&self, handle_request: H)
|
||||||
where
|
where
|
||||||
H: 'static + Send + Sync + Fn(R) -> F,
|
H: 'static + Send + Fn(User, R) -> F,
|
||||||
F: 'static + Send + Sync + futures::Future<Output = Result<R::Response>>,
|
F: 'static + Send + futures::Future<Output = Result<R::Response>>,
|
||||||
R: Request;
|
R: Request;
|
||||||
|
|
||||||
|
fn create_room(&self, room: &RoomName) -> BoxFuture<Result<()>>;
|
||||||
|
fn grant_room_access(&self, room: &RoomName, user: &str) -> RoomToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ClientNetwork: 'static + Send + Sync {
|
pub trait ClientNetwork: 'static + Send + Sync {
|
||||||
|
@ -212,49 +226,67 @@ impl<N: ClientNetwork> Client<N> {
|
||||||
fn handle_remote_operation(&self, repo_id: RepoId, operation: Operation) {}
|
fn handle_remote_operation(&self, repo_id: RepoId, operation: Operation) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
struct Server<N> {
|
||||||
struct Server {
|
|
||||||
db: Db,
|
db: Db,
|
||||||
|
network: Arc<N>,
|
||||||
repo_ids_by_name: Arc<Mutex<HashMap<Arc<str>, RepoId>>>,
|
repo_ids_by_name: Arc<Mutex<HashMap<Arc<str>, RepoId>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Server {
|
impl<N: ServerNetwork> Clone for Server<N> {
|
||||||
fn new(network: impl ServerNetwork) -> Self {
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
db: self.db.clone(),
|
||||||
|
network: self.network.clone(),
|
||||||
|
repo_ids_by_name: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: ServerNetwork> Server<N> {
|
||||||
|
fn new(network: N) -> Self {
|
||||||
let this = Self {
|
let this = Self {
|
||||||
db: Db::new(),
|
db: Db::new(),
|
||||||
|
network: Arc::new(network),
|
||||||
repo_ids_by_name: Default::default(),
|
repo_ids_by_name: Default::default(),
|
||||||
};
|
};
|
||||||
this.on_request(network, Self::handle_publish_repo);
|
this.on_request(Self::handle_publish_repo);
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_request<F, Fut, R, S>(&self, network: S, handle_request: F)
|
fn on_request<F, Fut, R>(&self, handle_request: F)
|
||||||
where
|
where
|
||||||
F: 'static + Send + Sync + Fn(Self, R) -> Fut,
|
F: 'static + Send + Fn(Self, User, R) -> Fut,
|
||||||
Fut: 'static + Send + Sync + Future<Output = Result<R::Response>>,
|
Fut: 'static + Send + Future<Output = Result<R::Response>>,
|
||||||
R: Request,
|
R: Request,
|
||||||
S: ServerNetwork,
|
|
||||||
{
|
{
|
||||||
network.on_request({
|
self.network.on_request({
|
||||||
let this = self.clone();
|
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(
|
async fn handle_publish_repo(
|
||||||
self,
|
self,
|
||||||
|
user: User,
|
||||||
request: messages::PublishRepo,
|
request: messages::PublishRepo,
|
||||||
) -> Result<messages::PublishRepoResponse> {
|
) -> Result<messages::PublishRepoResponse> {
|
||||||
// TODO: handle repositories that had already been published.
|
// TODO: handle repositories that had already been published.
|
||||||
match self.repo_ids_by_name.lock().entry(request.name) {
|
match self.repo_ids_by_name.lock().entry(request.name.clone()) {
|
||||||
hash_map::Entry::Occupied(_) => Err(anyhow!("repo name taken")),
|
hash_map::Entry::Occupied(_) => return Err(anyhow!("repo name taken")),
|
||||||
hash_map::Entry::Vacant(entry) => {
|
hash_map::Entry::Vacant(entry) => {
|
||||||
let mut db = self.db.snapshot.lock();
|
let mut db = self.db.snapshot.lock();
|
||||||
db.repos.insert(request.id, Default::default());
|
db.repos.insert(request.id, Default::default());
|
||||||
entry.insert(request.id);
|
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<OperationId, BranchSnapshot>,
|
branches: TreeMap<OperationId, BranchSnapshot>,
|
||||||
operations: TreeMap<OperationId, Operation>,
|
operations: TreeMap<OperationId, Operation>,
|
||||||
revisions: TreeMap<RevisionId, Revision>,
|
revisions: TreeMap<RevisionId, Revision>,
|
||||||
name: Option<Arc<str>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RepoSnapshot {
|
impl RepoSnapshot {
|
||||||
fn new(replica_id: ReplicaId) -> Self {
|
fn new(replica_id: ReplicaId) -> Self {
|
||||||
Self {
|
Self {
|
||||||
last_operation_id: OperationId::new(replica_id),
|
last_operation_id: OperationId::new(replica_id),
|
||||||
branches: Default::default(),
|
..Default::default()
|
||||||
operations: Default::default(),
|
|
||||||
revisions: Default::default(),
|
|
||||||
name: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1003,7 +1031,7 @@ mod tests {
|
||||||
let network = TestNetwork::new(deterministic.build_background());
|
let network = TestNetwork::new(deterministic.build_background());
|
||||||
let server = Server::new(network.server());
|
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 repo_a = client_a.create_repo();
|
||||||
let branch_a = repo_a.create_empty_branch("main");
|
let branch_a = repo_a.create_empty_branch("main");
|
||||||
|
|
||||||
|
@ -1017,7 +1045,7 @@ mod tests {
|
||||||
assert_eq!(doc2.text().to_string(), "def");
|
assert_eq!(doc2.text().to_string(), "def");
|
||||||
|
|
||||||
client_a.publish_repo(&repo_a, "repo-1").await.unwrap();
|
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();
|
let repo_b = db_b.clone_repo("repo-1").await.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{Message, RepoId, Request, RoomCredentials};
|
use crate::{RepoId, Request, RoomCredentials};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{AnchorRange, Message, OperationId, RevisionId};
|
use crate::{AnchorRange, OperationId, RevisionId};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
ClientNetwork, ClientRoom, Message, RoomCredentials, RoomName, RoomToken, ServerNetwork,
|
ClientNetwork, ClientRoom, Message, RoomCredentials, RoomName, RoomToken, ServerNetwork, User,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
|
@ -27,8 +27,13 @@ impl TestNetwork {
|
||||||
TestServer(self.0.clone())
|
TestServer(self.0.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn client(&self) -> TestClient {
|
pub fn client(&self, login: impl Into<Arc<str>>) -> TestClient {
|
||||||
TestClient(self.0.clone())
|
TestClient {
|
||||||
|
user: User {
|
||||||
|
login: login.into(),
|
||||||
|
},
|
||||||
|
network: self.0.clone(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +41,7 @@ struct NetworkState {
|
||||||
executor: Arc<Background>,
|
executor: Arc<Background>,
|
||||||
request_handlers: BTreeMap<
|
request_handlers: BTreeMap<
|
||||||
TypeId,
|
TypeId,
|
||||||
Box<dyn Send + Fn(Box<dyn Any>) -> BoxFuture<'static, Result<Box<dyn Any>>>>,
|
Box<dyn Send + Fn(User, Box<dyn Any>) -> BoxFuture<'static, Result<Box<dyn Any>>>>,
|
||||||
>,
|
>,
|
||||||
rooms: BTreeMap<RoomName, Room>,
|
rooms: BTreeMap<RoomName, Room>,
|
||||||
}
|
}
|
||||||
|
@ -50,15 +55,15 @@ pub struct TestServer(Arc<Mutex<NetworkState>>);
|
||||||
impl ServerNetwork for TestServer {
|
impl ServerNetwork for TestServer {
|
||||||
fn on_request<H, F, R>(&self, handle_request: H)
|
fn on_request<H, F, R>(&self, handle_request: H)
|
||||||
where
|
where
|
||||||
H: 'static + Send + Sync + Fn(R) -> F,
|
H: 'static + Send + Fn(User, R) -> F,
|
||||||
F: 'static + Send + Sync + futures::Future<Output = Result<R::Response>>,
|
F: 'static + Send + futures::Future<Output = Result<R::Response>>,
|
||||||
R: crate::Request,
|
R: crate::Request,
|
||||||
{
|
{
|
||||||
self.0.lock().request_handlers.insert(
|
self.0.lock().request_handlers.insert(
|
||||||
TypeId::of::<R>(),
|
TypeId::of::<R>(),
|
||||||
Box::new(move |request| {
|
Box::new(move |user, request| {
|
||||||
let request = request.downcast::<R>().unwrap();
|
let request = request.downcast::<R>().unwrap();
|
||||||
let response = handle_request(*request);
|
let response = handle_request(user, *request);
|
||||||
async move {
|
async move {
|
||||||
response
|
response
|
||||||
.await
|
.await
|
||||||
|
@ -68,9 +73,20 @@ impl ServerNetwork for TestServer {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_room(&self, room: &RoomName) -> BoxFuture<Result<()>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn grant_room_access(&self, room: &RoomName, user: &str) -> RoomToken {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TestClient(Arc<Mutex<NetworkState>>);
|
pub struct TestClient {
|
||||||
|
user: User,
|
||||||
|
network: Arc<Mutex<NetworkState>>,
|
||||||
|
}
|
||||||
|
|
||||||
impl ClientNetwork for TestClient {
|
impl ClientNetwork for TestClient {
|
||||||
type Room = TestClientRoom;
|
type Room = TestClientRoom;
|
||||||
|
@ -79,7 +95,7 @@ impl ClientNetwork for TestClient {
|
||||||
&self,
|
&self,
|
||||||
request: R,
|
request: R,
|
||||||
) -> futures::future::BoxFuture<anyhow::Result<R::Response>> {
|
) -> futures::future::BoxFuture<anyhow::Result<R::Response>> {
|
||||||
let network = self.0.lock();
|
let network = self.network.lock();
|
||||||
let executor = network.executor.clone();
|
let executor = network.executor.clone();
|
||||||
let request = network
|
let request = network
|
||||||
.request_handlers
|
.request_handlers
|
||||||
|
@ -87,7 +103,7 @@ impl ClientNetwork for TestClient {
|
||||||
.expect(&format!(
|
.expect(&format!(
|
||||||
"handler for request {} not found",
|
"handler for request {} not found",
|
||||||
type_name::<R>()
|
type_name::<R>()
|
||||||
))(Box::new(request));
|
))(self.user.clone(), Box::new(request));
|
||||||
async move {
|
async move {
|
||||||
executor.simulate_random_delay().await;
|
executor.simulate_random_delay().await;
|
||||||
let response = request
|
let response = request
|
||||||
|
@ -104,7 +120,7 @@ impl ClientNetwork for TestClient {
|
||||||
outbox: Default::default(),
|
outbox: Default::default(),
|
||||||
credentials,
|
credentials,
|
||||||
message_handlers: Default::default(),
|
message_handlers: Default::default(),
|
||||||
network: self.0.clone(),
|
network: self.network.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue