mirror of
https://github.com/hexedtech/codemp.git
synced 2025-04-02 09:31:33 +02:00
117 lines
3.4 KiB
Rust
117 lines
3.4 KiB
Rust
use std::{sync::Arc, collections::BTreeMap};
|
|
|
|
use tonic::transport::Channel;
|
|
|
|
use crate::{
|
|
cursor::{worker::CursorControllerWorker, controller::CursorController},
|
|
proto::{
|
|
buffer_client::BufferClient, cursor_client::CursorClient, UserIdentity, BufferPayload,
|
|
},
|
|
CodempError, ControllerWorker, buffer::{controller::BufferController, worker::BufferControllerWorker},
|
|
};
|
|
|
|
|
|
pub struct CodempClient {
|
|
id: String,
|
|
client: ServiceClients,
|
|
workspace: Option<Workspace>,
|
|
}
|
|
|
|
struct ServiceClients {
|
|
buffer: BufferClient<Channel>,
|
|
cursor: CursorClient<Channel>,
|
|
}
|
|
|
|
struct Workspace {
|
|
cursor: Arc<CursorController>,
|
|
buffers: BTreeMap<String, Arc<BufferController>>,
|
|
}
|
|
|
|
|
|
impl CodempClient {
|
|
pub async fn new(dst: &str) -> Result<Self, tonic::transport::Error> {
|
|
let buffer = BufferClient::connect(dst.to_string()).await?;
|
|
let cursor = CursorClient::connect(dst.to_string()).await?;
|
|
let id = uuid::Uuid::new_v4().to_string();
|
|
|
|
Ok(CodempClient { id, client: ServiceClients { buffer, cursor}, workspace: None })
|
|
}
|
|
|
|
pub fn get_cursor(&self) -> Option<Arc<CursorController>> {
|
|
Some(self.workspace?.cursor.clone())
|
|
}
|
|
|
|
pub fn get_buffer(&self, path: &str) -> Option<Arc<BufferController>> {
|
|
self.workspace?.buffers.get(path).cloned()
|
|
}
|
|
|
|
pub async fn join(&mut self, _session: &str) -> Result<Arc<CursorController>, CodempError> {
|
|
// TODO there is no real workspace handling in codemp server so it behaves like one big global
|
|
// session. I'm still creating this to start laying out the proper use flow
|
|
let stream = self.client.cursor.listen(UserIdentity { id: "".into() }).await?.into_inner();
|
|
|
|
let controller = CursorControllerWorker::new(self.id.clone());
|
|
let client = self.client.cursor.clone();
|
|
|
|
let handle = Arc::new(controller.subscribe());
|
|
|
|
tokio::spawn(async move {
|
|
tracing::debug!("cursor worker started");
|
|
controller.work(client, stream).await;
|
|
tracing::debug!("cursor worker stopped");
|
|
});
|
|
|
|
self.workspace = Some(
|
|
Workspace {
|
|
cursor: handle.clone(),
|
|
buffers: BTreeMap::new()
|
|
}
|
|
);
|
|
|
|
Ok(handle)
|
|
}
|
|
|
|
pub async fn create(&mut self, path: &str, content: Option<&str>) -> Result<(), CodempError> {
|
|
if let Some(workspace) = &self.workspace {
|
|
self.client.buffer
|
|
.create(BufferPayload {
|
|
user: self.id.clone(),
|
|
path: path.to_string(),
|
|
content: content.map(|x| x.to_string()),
|
|
}).await?;
|
|
|
|
Ok(())
|
|
} else {
|
|
Err(CodempError::InvalidState { msg: "join a workspace first".into() })
|
|
}
|
|
}
|
|
|
|
pub async fn attach(&mut self, path: &str, content: Option<&str>) -> Result<Arc<BufferController>, CodempError> {
|
|
if let Some(workspace) = &mut self.workspace {
|
|
let mut client = self.client.buffer.clone();
|
|
let req = BufferPayload {
|
|
path: path.to_string(), user: self.id.clone(), content: None
|
|
};
|
|
|
|
let content = client.sync(req.clone()).await?.into_inner().content;
|
|
|
|
let stream = client.attach(req).await?.into_inner();
|
|
|
|
let controller = BufferControllerWorker::new(self.id.clone(), &content, path);
|
|
let handler = Arc::new(controller.subscribe());
|
|
|
|
let _path = path.to_string();
|
|
tokio::spawn(async move {
|
|
tracing::debug!("buffer[{}] worker started", _path);
|
|
controller.work(client, stream).await;
|
|
tracing::debug!("buffer[{}] worker stopped", _path);
|
|
});
|
|
|
|
workspace.buffers.insert(path.to_string(), handler.clone());
|
|
|
|
Ok(handler)
|
|
} else {
|
|
Err(CodempError::InvalidState { msg: "join a workspace first".into() })
|
|
}
|
|
}
|
|
}
|