diff --git a/src/buffer/controller.rs b/src/buffer/controller.rs index bf861e7..9917ad7 100644 --- a/src/buffer/controller.rs +++ b/src/buffer/controller.rs @@ -2,7 +2,7 @@ use operational_transform::OperationSeq; use tokio::sync::{watch, mpsc, broadcast, Mutex}; use tonic::async_trait; -use crate::{Controller, CodempError}; +use crate::{Controller, Error}; use crate::buffer::factory::{leading_noop, tailing_noop, OperationFactory}; use super::TextChange; @@ -34,7 +34,7 @@ impl OperationFactory for BufferController { impl Controller for BufferController { type Input = OperationSeq; - async fn recv(&self) -> Result { + async fn recv(&self) -> Result { let op = self.stream.lock().await.recv().await?; let after = self.content.borrow().clone(); let skip = leading_noop(op.ops()) as usize; @@ -45,7 +45,7 @@ impl Controller for BufferController { Ok(TextChange { span, content }) } - async fn send(&self, op: OperationSeq) -> Result<(), CodempError> { + async fn send(&self, op: OperationSeq) -> Result<(), Error> { Ok(self.operations.send(op).await?) } } diff --git a/src/client.rs b/src/client.rs index 453c944..24508dd 100644 --- a/src/client.rs +++ b/src/client.rs @@ -7,17 +7,17 @@ use crate::{ proto::{ buffer_client::BufferClient, cursor_client::CursorClient, UserIdentity, BufferPayload, }, - CodempError, ControllerWorker, buffer::{controller::BufferController, worker::BufferControllerWorker}, + Error, ControllerWorker, buffer::{controller::BufferController, worker::BufferControllerWorker}, }; -pub struct CodempClient { +pub struct Client { id: String, - client: ServiceClients, + client: Services, workspace: Option, } -struct ServiceClients { +struct Services { buffer: BufferClient, cursor: CursorClient, } @@ -28,13 +28,13 @@ struct Workspace { } -impl CodempClient { +impl Client { pub async fn new(dst: &str) -> Result { 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 }) + Ok(Client { id, client: Services { buffer, cursor}, workspace: None }) } pub fn get_cursor(&self) -> Option> { @@ -45,7 +45,7 @@ impl CodempClient { self.workspace.as_ref()?.buffers.get(path).cloned() } - pub async fn join(&mut self, _session: &str) -> Result, CodempError> { + pub async fn join(&mut self, _session: &str) -> Result, Error> { // 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(); @@ -71,7 +71,7 @@ impl CodempClient { Ok(handle) } - pub async fn create(&mut self, path: &str, content: Option<&str>) -> Result<(), CodempError> { + pub async fn create(&mut self, path: &str, content: Option<&str>) -> Result<(), Error> { if let Some(_workspace) = &self.workspace { self.client.buffer .create(BufferPayload { @@ -82,11 +82,11 @@ impl CodempClient { Ok(()) } else { - Err(CodempError::InvalidState { msg: "join a workspace first".into() }) + Err(Error::InvalidState { msg: "join a workspace first".into() }) } } - pub async fn attach(&mut self, path: &str) -> Result, CodempError> { + pub async fn attach(&mut self, path: &str) -> Result, Error> { if let Some(workspace) = &mut self.workspace { let mut client = self.client.buffer.clone(); let req = BufferPayload { @@ -111,7 +111,7 @@ impl CodempClient { Ok(handler) } else { - Err(CodempError::InvalidState { msg: "join a workspace first".into() }) + Err(Error::InvalidState { msg: "join a workspace first".into() }) } } } diff --git a/src/cursor/controller.rs b/src/cursor/controller.rs index d553314..2c650e9 100644 --- a/src/cursor/controller.rs +++ b/src/cursor/controller.rs @@ -1,7 +1,7 @@ use tokio::sync::{mpsc, broadcast::{self, error::RecvError}, Mutex}; use tonic::async_trait; -use crate::{proto::{CursorPosition, CursorEvent}, CodempError, Controller}; +use crate::{proto::{CursorPosition, CursorEvent}, Error, Controller}; pub struct CursorController { uid: String, @@ -23,7 +23,7 @@ impl CursorController { impl Controller for CursorController { type Input = CursorPosition; - async fn send(&self, cursor: CursorPosition) -> Result<(), CodempError> { + async fn send(&self, cursor: CursorPosition) -> Result<(), Error> { Ok(self.op.send(CursorEvent { user: self.uid.clone(), position: Some(cursor), @@ -32,11 +32,11 @@ impl Controller for CursorController { // TODO is this cancelable? so it can be used in tokio::select! // TODO is the result type overkill? should be an option? - async fn recv(&self) -> Result { + async fn recv(&self) -> Result { let mut stream = self.stream.lock().await; match stream.recv().await { Ok(x) => Ok(x), - Err(RecvError::Closed) => Err(CodempError::Channel { send: false }), + Err(RecvError::Closed) => Err(Error::Channel { send: false }), Err(RecvError::Lagged(n)) => { tracing::error!("cursor channel lagged behind, skipping {} events", n); Ok(stream.recv().await.expect("could not receive after lagging")) diff --git a/src/errors.rs b/src/errors.rs index 6b923a7..5982ace 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,4 +1,4 @@ -use std::{error::Error, fmt::Display}; +use std::{error::Error as StdError, fmt::Display}; use tokio::sync::{mpsc, broadcast}; use tonic::{Status, Code}; @@ -20,7 +20,7 @@ where E : std::fmt::Display { // TODO split this into specific errors for various parts of the library #[derive(Debug)] -pub enum CodempError { +pub enum Error { Transport { status: Code, message: String, @@ -38,9 +38,9 @@ pub enum CodempError { }, } -impl Error for CodempError {} +impl StdError for Error {} -impl Display for CodempError { +impl Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Transport { status, message } => write!(f, "Transport error: ({}) {}", status, message), @@ -50,28 +50,28 @@ impl Display for CodempError { } } -impl From for CodempError { +impl From for Error { fn from(status: Status) -> Self { - CodempError::Transport { status: status.code(), message: status.message().to_string() } + Error::Transport { status: status.code(), message: status.message().to_string() } } } -impl From for CodempError { +impl From for Error { fn from(err: tonic::transport::Error) -> Self { - CodempError::Transport { + Error::Transport { status: Code::Unknown, message: format!("underlying transport error: {:?}", err) } } } -impl From> for CodempError { +impl From> for Error { fn from(_value: mpsc::error::SendError) -> Self { - CodempError::Channel { send: true } + Error::Channel { send: true } } } -impl From for CodempError { +impl From for Error { fn from(_value: broadcast::error::RecvError) -> Self { - CodempError::Channel { send: false } + Error::Channel { send: false } } } diff --git a/src/instance.rs b/src/instance.rs index 0feec6a..3ca92a2 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -4,7 +4,7 @@ use tokio::sync::Mutex; use crate::{ buffer::controller::BufferController, - errors::CodempError, client::CodempClient, cursor::controller::CursorController, + errors::Error, client::Client, cursor::controller::CursorController, }; @@ -16,7 +16,7 @@ lazy_static::lazy_static! { } pub struct Instance { - client: Mutex>, + client: Mutex>, } impl Default for Instance { @@ -28,52 +28,52 @@ impl Default for Instance { // TODO these methods repeat a lot of code but Mutex makes it hard to simplify impl Instance { - pub async fn connect(&self, addr: &str) -> Result<(), CodempError> { - *self.client.lock().await = Some(CodempClient::new(addr).await?); + pub async fn connect(&self, addr: &str) -> Result<(), Error> { + *self.client.lock().await = Some(Client::new(addr).await?); Ok(()) } - pub async fn join(&self, session: &str) -> Result<(), CodempError> { + pub async fn join(&self, session: &str) -> Result<(), Error> { self.client .lock() .await .as_mut() - .ok_or(CodempError::InvalidState { msg: "connect first".into() })? + .ok_or(Error::InvalidState { msg: "connect first".into() })? .join(session) .await?; Ok(()) } - pub async fn create(&self, path: &str, content: Option<&str>) -> Result<(), CodempError> { + pub async fn create(&self, path: &str, content: Option<&str>) -> Result<(), Error> { self.client .lock() .await .as_mut() - .ok_or(CodempError::InvalidState { msg: "connect first".into() })? + .ok_or(Error::InvalidState { msg: "connect first".into() })? .create(path, content) .await?; Ok(()) } - pub async fn get_cursor(&self) -> Result, CodempError> { + pub async fn get_cursor(&self) -> Result, Error> { self.client .lock() .await .as_mut() - .ok_or(CodempError::InvalidState { msg: "connect first".into() })? + .ok_or(Error::InvalidState { msg: "connect first".into() })? .get_cursor() - .ok_or(CodempError::InvalidState { msg: "join a workspace first".into() }) + .ok_or(Error::InvalidState { msg: "join a workspace first".into() }) } - pub async fn get_buffer(&self, path: &str) -> Result, CodempError> { + pub async fn get_buffer(&self, path: &str) -> Result, Error> { self.client .lock() .await .as_mut() - .ok_or(CodempError::InvalidState { msg: "connect first".into() })? + .ok_or(Error::InvalidState { msg: "connect first".into() })? .get_buffer(path) - .ok_or(CodempError::InvalidState { msg: "join a workspace or create requested buffer first".into() }) + .ok_or(Error::InvalidState { msg: "join a workspace or create requested buffer first".into() }) } } diff --git a/src/lib.rs b/src/lib.rs index 0975600..9c835a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,8 @@ pub mod client; #[cfg(feature = "static")] pub mod instance; +pub mod prelude; + pub use tonic; pub use tokio; pub use operational_transform as ot; @@ -18,7 +20,7 @@ pub mod proto { tonic::include_proto!("codemp.cursor"); } -pub use errors::CodempError; +pub use errors::Error; #[tonic::async_trait] // TODO move this somewhere? pub(crate) trait ControllerWorker { @@ -34,6 +36,6 @@ pub(crate) trait ControllerWorker { pub trait Controller { type Input; - async fn send(&self, x: Self::Input) -> Result<(), CodempError>; - async fn recv(&self) -> Result; + async fn send(&self, x: Self::Input) -> Result<(), Error>; + async fn recv(&self) -> Result; } diff --git a/src/prelude.rs b/src/prelude.rs new file mode 100644 index 0000000..67c5fe0 --- /dev/null +++ b/src/prelude.rs @@ -0,0 +1,11 @@ +pub use crate::client::Client as CodempClient; +pub use crate::errors::Error as CodempError; + +pub use crate::cursor::controller::CursorController as CodempCursorController; +pub use crate::buffer::controller::BufferController as CodempBufferController; + +pub use crate::buffer::TextChange as CodempTextChange; +pub use crate::proto::CursorPosition as CodempCursorPosition; + +#[cfg(feature = "static")] +pub use crate::instance::Instance as CodempInstance;