feat: experimental select_buffer client api

This commit is contained in:
əlemi 2023-11-17 17:00:10 +01:00
parent 99ba5f708d
commit 8dc3538f32
3 changed files with 58 additions and 20 deletions

View file

@ -25,6 +25,7 @@ serde_json = { version = "1", optional = true }
tokio-stream = { version = "0.1", optional = true } tokio-stream = { version = "0.1", optional = true }
# global # global
lazy_static = { version = "1.4", optional = true } lazy_static = { version = "1.4", optional = true }
futures = "0.3"
[build-dependencies] [build-dependencies]
tonic-build = "0.9" tonic-build = "0.9"

View file

@ -3,10 +3,13 @@
//! codemp client manager, containing grpc services //! codemp client manager, containing grpc services
use std::{sync::Arc, collections::BTreeMap}; use std::{sync::Arc, collections::BTreeMap};
use futures::stream::FuturesUnordered;
use tokio_stream::StreamExt;
use tonic::transport::Channel; use tonic::transport::Channel;
use crate::{ use crate::{
api::Controller,
cursor::{worker::CursorControllerWorker, controller::CursorController}, cursor::{worker::CursorControllerWorker, controller::CursorController},
proto::{ proto::{
buffer_client::BufferClient, cursor_client::CursorClient, UserIdentity, BufferPayload, buffer_client::BufferClient, cursor_client::CursorClient, UserIdentity, BufferPayload,
@ -76,7 +79,7 @@ impl Client {
/// ///
/// to interact with such workspace [crate::api::Controller::send] cursor events or /// to interact with such workspace [crate::api::Controller::send] cursor events or
/// [crate::api::Controller::recv] for events on the associated [crate::cursor::Controller]. /// [crate::api::Controller::recv] for events on the associated [crate::cursor::Controller].
pub async fn join(&mut self, _session: &str) -> Result<Arc<CursorController>, Error> { pub async fn join(&mut self, _session: &str) -> crate::Result<Arc<CursorController>> {
// TODO there is no real workspace handling in codemp server so it behaves like one big global // 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 // 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 stream = self.client.cursor.listen(UserIdentity { id: "".into() }).await?.into_inner();
@ -103,7 +106,7 @@ impl Client {
} }
/// create a new buffer in current workspace, with optional given content /// create a new buffer in current workspace, with optional given content
pub async fn create(&mut self, path: &str, content: Option<&str>) -> Result<(), Error> { pub async fn create(&mut self, path: &str, content: Option<&str>) -> crate::Result<()> {
if let Some(_workspace) = &self.workspace { if let Some(_workspace) = &self.workspace {
self.client.buffer self.client.buffer
.create(BufferPayload { .create(BufferPayload {
@ -122,7 +125,7 @@ impl Client {
/// ///
/// to interact with such buffer use [crate::api::Controller::send] or /// to interact with such buffer use [crate::api::Controller::send] or
/// [crate::api::Controller::recv] to exchange [crate::api::TextChange] /// [crate::api::Controller::recv] to exchange [crate::api::TextChange]
pub async fn attach(&mut self, path: &str) -> Result<Arc<BufferController>, Error> { pub async fn attach(&mut self, path: &str) -> crate::Result<Arc<BufferController>> {
if let Some(workspace) = &mut self.workspace { if let Some(workspace) = &mut self.workspace {
let mut client = self.client.buffer.clone(); let mut client = self.client.buffer.clone();
let req = BufferPayload { let req = BufferPayload {
@ -148,4 +151,24 @@ impl Client {
Err(Error::InvalidState { msg: "join a workspace first".into() }) Err(Error::InvalidState { msg: "join a workspace first".into() })
} }
} }
pub async fn select_buffer(&self) -> crate::Result<String> {
let mut futures = FuturesUnordered::new();
match &self.workspace {
None => Err(Error::InvalidState { msg: "join workspace first".into() }),
Some(workspace) => {
for (id, buffer) in workspace.buffers.iter() {
futures.push(async move {
buffer.poll().await?;
Ok::<&String, Error>(id)
})
}
match futures.next().await {
None => Err(Error::Deadlocked), // TODO shouldn't really happen???
Some(x) => Ok(x?.clone()),
}
}
}
}
} }

View file

@ -46,13 +46,13 @@ pub mod a_sync {
impl Instance { impl Instance {
/// connect to remote address instantiating a new client [crate::client::Client::new] /// connect to remote address instantiating a new client [crate::client::Client::new]
pub async fn connect(&self, addr: &str) -> Result<(), Error> { pub async fn connect(&self, addr: &str) -> crate::Result<()> {
*self.client.lock().await = Some(Client::new(addr).await?); *self.client.lock().await = Some(Client::new(addr).await?);
Ok(()) Ok(())
} }
/// threadsafe version of [crate::client::Client::join] /// threadsafe version of [crate::client::Client::join]
pub async fn join(&self, session: &str) -> Result<Arc<CursorController>, Error> { pub async fn join(&self, session: &str) -> crate::Result<Arc<CursorController>> {
self.client self.client
.lock().await .lock().await
.as_mut() .as_mut()
@ -62,7 +62,7 @@ pub mod a_sync {
} }
/// threadsafe version of [crate::client::Client::create] /// threadsafe version of [crate::client::Client::create]
pub async fn create(&self, path: &str, content: Option<&str>) -> Result<(), Error> { pub async fn create(&self, path: &str, content: Option<&str>) -> crate::Result<()> {
self.client self.client
.lock().await .lock().await
.as_mut() .as_mut()
@ -72,7 +72,7 @@ pub mod a_sync {
} }
/// threadsafe version of [crate::client::Client::attach] /// threadsafe version of [crate::client::Client::attach]
pub async fn attach(&self, path: &str) -> Result<Arc<BufferController>, Error> { pub async fn attach(&self, path: &str) -> crate::Result<Arc<BufferController>> {
self.client self.client
.lock().await .lock().await
.as_mut() .as_mut()
@ -82,7 +82,7 @@ pub mod a_sync {
} }
/// threadsafe version of [crate::client::Client::get_cursor] /// threadsafe version of [crate::client::Client::get_cursor]
pub async fn get_cursor(&self) -> Result<Arc<CursorController>, Error> { pub async fn get_cursor(&self) -> crate::Result<Arc<CursorController>> {
self.client self.client
.lock().await .lock().await
.as_mut() .as_mut()
@ -92,7 +92,7 @@ pub mod a_sync {
} }
/// threadsafe version of [crate::client::Client::get_buffer] /// threadsafe version of [crate::client::Client::get_buffer]
pub async fn get_buffer(&self, path: &str) -> Result<Arc<BufferController>, Error> { pub async fn get_buffer(&self, path: &str) -> crate::Result<Arc<BufferController>> {
self.client self.client
.lock().await .lock().await
.as_mut() .as_mut()
@ -102,7 +102,7 @@ pub mod a_sync {
} }
/// threadsafe version of [crate::client::Client::leave_workspace] /// threadsafe version of [crate::client::Client::leave_workspace]
pub async fn leave_workspace(&self) -> Result<(), Error> { pub async fn leave_workspace(&self) -> crate::Result<()> {
self.client self.client
.lock().await .lock().await
.as_mut() .as_mut()
@ -112,7 +112,7 @@ pub mod a_sync {
} }
/// threadsafe version of [crate::client::Client::disconnect_buffer] /// threadsafe version of [crate::client::Client::disconnect_buffer]
pub async fn disconnect_buffer(&self, path: &str) -> Result<bool, Error> { pub async fn disconnect_buffer(&self, path: &str) -> crate::Result<bool> {
let res = self.client let res = self.client
.lock().await .lock().await
.as_mut() .as_mut()
@ -120,6 +120,16 @@ pub mod a_sync {
.disconnect_buffer(path); .disconnect_buffer(path);
Ok(res) Ok(res)
} }
pub async fn select_buffer(&self) -> crate::Result<String> {
let res = self.client
.lock().await
.as_ref()
.ok_or(Error::InvalidState { msg: "connect first".into() })?
.select_buffer()
.await?;
Ok(res)
}
} }
} }
@ -135,7 +145,7 @@ pub mod sync {
buffer::controller::BufferController buffer::controller::BufferController
}; };
/// persistant session manager for codemp client /// persistent session manager for codemp client
/// ///
/// will hold a std mutex over an optional client, and drop its reference when disconnecting. /// will hold a std mutex over an optional client, and drop its reference when disconnecting.
/// also contains a tokio runtime to execute async futures on /// also contains a tokio runtime to execute async futures on
@ -157,7 +167,7 @@ pub mod sync {
} }
impl Instance { impl Instance {
fn if_client<T>(&self, op: impl FnOnce(&mut Client) -> T) -> Result<T, Error> { fn if_client<T>(&self, op: impl FnOnce(&mut Client) -> T) -> crate::Result<T> {
if let Some(c) = self.client.lock().expect("client mutex poisoned").as_mut() { if let Some(c) = self.client.lock().expect("client mutex poisoned").as_mut() {
Ok(op(c)) Ok(op(c))
} else { } else {
@ -175,38 +185,42 @@ pub mod sync {
} }
/// threadsafe and sync version of [crate::client::Client::join] /// threadsafe and sync version of [crate::client::Client::join]
pub fn join(&self, session: &str) -> Result<Arc<CursorController>, Error> { pub fn join(&self, session: &str) -> crate::Result<Arc<CursorController>> {
self.if_client(|c| self.rt().block_on(c.join(session)))? self.if_client(|c| self.rt().block_on(c.join(session)))?
} }
/// threadsafe and sync version of [crate::client::Client::create] /// threadsafe and sync version of [crate::client::Client::create]
pub fn create(&self, path: &str, content: Option<&str>) -> Result<(), Error> { pub fn create(&self, path: &str, content: Option<&str>) -> crate::Result<()> {
self.if_client(|c| self.rt().block_on(c.create(path, content)))? self.if_client(|c| self.rt().block_on(c.create(path, content)))?
} }
/// threadsafe and sync version of [crate::client::Client::attach] /// threadsafe and sync version of [crate::client::Client::attach]
pub fn attach(&self, path: &str) -> Result<Arc<BufferController>, Error> { pub fn attach(&self, path: &str) -> crate::Result<Arc<BufferController>> {
self.if_client(|c| self.rt().block_on(c.attach(path)))? self.if_client(|c| self.rt().block_on(c.attach(path)))?
} }
/// threadsafe and sync version of [crate::client::Client::get_cursor] /// threadsafe and sync version of [crate::client::Client::get_cursor]
pub fn get_cursor(&self) -> Result<Arc<CursorController>, Error> { pub fn get_cursor(&self) -> crate::Result<Arc<CursorController>> {
self.if_client(|c| c.get_cursor().ok_or(Error::InvalidState { msg: "join workspace first".into() }))? self.if_client(|c| c.get_cursor().ok_or(Error::InvalidState { msg: "join workspace first".into() }))?
} }
/// threadsafe and sync version of [crate::client::Client::get_buffer] /// threadsafe and sync version of [crate::client::Client::get_buffer]
pub fn get_buffer(&self, path: &str) -> Result<Arc<BufferController>, Error> { pub fn get_buffer(&self, path: &str) -> crate::Result<Arc<BufferController>> {
self.if_client(|c| c.get_buffer(path).ok_or(Error::InvalidState { msg: "join workspace or create requested buffer first".into() }))? self.if_client(|c| c.get_buffer(path).ok_or(Error::InvalidState { msg: "join workspace or create requested buffer first".into() }))?
} }
/// threadsafe and sync version of [crate::client::Client::leave_workspace] /// threadsafe and sync version of [crate::client::Client::leave_workspace]
pub fn leave_workspace(&self) -> Result<(), Error> { pub fn leave_workspace(&self) -> crate::Result<()> {
self.if_client(|c| c.leave_workspace()) self.if_client(|c| c.leave_workspace())
} }
/// threadsafe and sync version of [crate::client::Client::disconnect_buffer] /// threadsafe and sync version of [crate::client::Client::disconnect_buffer]
pub fn disconnect_buffer(&self, path: &str) -> Result<bool, Error> { pub fn disconnect_buffer(&self, path: &str) -> crate::Result<bool> {
self.if_client(|c| c.disconnect_buffer(path)) self.if_client(|c| c.disconnect_buffer(path))
} }
pub fn select_buffer(&self) -> crate::Result<String> {
self.if_client(|c| self.rt().block_on(c.select_buffer()))?
}
} }
} }