diff --git a/src/api/controller.rs b/src/api/controller.rs index 7b54b81..691c672 100644 --- a/src/api/controller.rs +++ b/src/api/controller.rs @@ -8,16 +8,17 @@ use crate::errors::ControllerResult; // note that we don't use thiserror's #[from] because we don't want the error structs to contain // these foreign types, and also we want these to be easily constructable -/// Asynchronous and thread-safe handle to a generic bidirectional stream. +/// Asynchronous and thread-safe handle to a generic bidirectional stream. Exists as a combination +/// of [`AsyncSender`] and [`AsyncReceiver`]. /// /// This generic trait is implemented by actors managing stream procedures, and will generally /// imply a background worker. /// -/// Events can be enqueued for dispatching without blocking with [`Controller::send`]. +/// Events can be enqueued for dispatching without blocking with [`AsyncSender::send`]. /// -/// For receiving events from the server, an asynchronous API with [`Controller::recv`] is -/// provided; if that is not feasible, consider using [`Controller::callback`] or, alternatively, -/// [`Controller::poll`] combined with [`Controller::try_recv`]. +/// For receiving events from the server, an asynchronous API with [`AsyncReceiver::recv`] is +/// provided; if that is not feasible, consider using [`AsyncReceiver::callback`] or, alternatively, +/// [`AsyncReceiver::poll`] combined with [`AsyncReceiver::try_recv`]. /// /// Every [`Controller`]'s worker will stop cleanly when all references to its [`Controller`] have /// been dropped. @@ -25,10 +26,26 @@ use crate::errors::ControllerResult; /// [`crate::ext::select_buffer`] may provide a useful helper for managing multiple controllers. #[allow(async_fn_in_trait)] #[cfg_attr(feature = "async-trait", async_trait::async_trait)] -pub trait Controller : Sized + Send + Sync { +pub trait Controller : AsyncSender + AsyncReceiver {} + +/// Asynchronous and thread-safe handle to send data over a stream. +/// See [`Controller`]'s documentation for details. +/// +/// Details about the receiving end are left to the implementor. +#[allow(async_fn_in_trait)] +#[cfg_attr(feature = "async-trait", async_trait::async_trait)] +pub trait AsyncSender : Sized + Send + Sync { /// Enqueue a new value to be sent to all other users. async fn send(&self, x: T) -> ControllerResult<()>; +} +/// Asynchronous and thread-safe handle to receive data from a stream. +/// See [`Controller`]'s documentation for details. +/// +/// Details about the sender are left to the implementor. +#[allow(async_fn_in_trait)] +#[cfg_attr(feature = "async-trait", async_trait::async_trait)] +pub trait AsyncReceiver : Sized + Send + Sync { /// Block until a value is available and returns it. async fn recv(&self) -> ControllerResult { loop { diff --git a/src/buffer/controller.rs b/src/buffer/controller.rs index 73229be..bcf115e 100644 --- a/src/buffer/controller.rs +++ b/src/buffer/controller.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use diamond_types::LocalVersion; use tokio::sync::{mpsc, oneshot, watch}; -use crate::api::controller::{Controller, ControllerCallback}; +use crate::api::controller::{AsyncReceiver, AsyncSender, Controller, ControllerCallback}; use crate::api::TextChange; use crate::errors::ControllerResult; use crate::ext::InternallyMutable; @@ -53,7 +53,21 @@ pub(crate) struct BufferControllerInner { } #[cfg_attr(feature = "async-trait", async_trait::async_trait)] -impl Controller for BufferController { +impl Controller for BufferController {} + +#[cfg_attr(feature = "async-trait", async_trait::async_trait)] +impl AsyncSender for BufferController { + async fn send(&self, op: TextChange) -> ControllerResult<()> { + // we let the worker do the updating to the last version and send it back. + let (tx, rx) = oneshot::channel(); + self.0.ops_in.send((op, tx))?; + self.0.last_update.set(rx.await?); + Ok(()) + } +} + +#[cfg_attr(feature = "async-trait", async_trait::async_trait)] +impl AsyncReceiver for BufferController { async fn poll(&self) -> ControllerResult<()> { if self.0.last_update.get() != *self.0.latest_version.borrow() { return Ok(()); @@ -80,14 +94,6 @@ impl Controller for BufferController { Ok(change) } - async fn send(&self, op: TextChange) -> ControllerResult<()> { - // we let the worker do the updating to the last version and send it back. - let (tx, rx) = oneshot::channel(); - self.0.ops_in.send((op, tx))?; - self.0.last_update.set(rx.await?); - Ok(()) - } - fn callback(&self, cb: impl Into>) { if self.0.callback.send(Some(cb.into())).is_err() { // TODO should we panic? we failed what we were supposed to do diff --git a/src/cursor/controller.rs b/src/cursor/controller.rs index a8dc862..e16c1d1 100644 --- a/src/cursor/controller.rs +++ b/src/cursor/controller.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use tokio::sync::{mpsc, oneshot, watch}; use crate::{ - api::{controller::ControllerCallback, Controller, Cursor}, + api::{controller::{AsyncReceiver, AsyncSender, ControllerCallback}, Controller, Cursor}, errors::ControllerResult, }; use codemp_proto::{ @@ -31,7 +31,10 @@ pub(crate) struct CursorControllerInner { } #[cfg_attr(feature = "async-trait", async_trait::async_trait)] -impl Controller for CursorController { +impl Controller for CursorController {} + +#[cfg_attr(feature = "async-trait", async_trait::async_trait)] +impl AsyncSender for CursorController { async fn send(&self, mut cursor: Cursor) -> ControllerResult<()> { if cursor.start > cursor.end { std::mem::swap(&mut cursor.start, &mut cursor.end); @@ -54,7 +57,10 @@ impl Controller for CursorController { }) .await?) } +} +#[cfg_attr(feature = "async-trait", async_trait::async_trait)] +impl AsyncReceiver for CursorController { async fn try_recv(&self) -> ControllerResult> { let (tx, rx) = oneshot::channel(); self.0.stream.send(tx).await?; diff --git a/src/ext.rs b/src/ext.rs index 7daa3a5..7228793 100644 --- a/src/ext.rs +++ b/src/ext.rs @@ -1,7 +1,7 @@ //! ### Extensions //! Contains a number of utils used internally or that may be of general interest. -use crate::{api::Controller, errors::ControllerResult}; +use crate::{api::controller::AsyncReceiver, errors::ControllerResult}; use tokio::sync::mpsc; /// Poll all given buffer controllers and wait, returning the first one ready.