mirror of
https://github.com/hexedtech/codemp.git
synced 2024-11-22 15:24:48 +01:00
docs: updated usage and references
This commit is contained in:
parent
5be1a7b901
commit
39f2bd6ac2
5 changed files with 35 additions and 43 deletions
|
@ -20,20 +20,22 @@ pub(crate) trait ControllerWorker<T : Sized + Send + Sync> {
|
||||||
/// this generic trait is implemented by actors managing stream procedures.
|
/// this generic trait is implemented by actors managing stream procedures.
|
||||||
/// events can be enqueued for dispatching without blocking ([Controller::send]), and an async blocking
|
/// events can be enqueued for dispatching without blocking ([Controller::send]), and an async blocking
|
||||||
/// api ([Controller::recv]) is provided to wait for server events. Additional sync blocking
|
/// api ([Controller::recv]) is provided to wait for server events. Additional sync blocking
|
||||||
/// ([Controller::blocking_recv]) and callback-based ([Controller::callback]) are implemented.
|
/// ([Controller::blocking_recv]) is implemented if feature `sync` is enabled.
|
||||||
///
|
///
|
||||||
/// * if possible, prefer a pure [Controller::recv] consumer
|
/// * if possible, prefer a pure [Controller::recv] consumer, awaiting for events
|
||||||
/// * a second possibility in preference is using a [Controller::callback]
|
/// * if async is not feasible a [Controller::poll]/[Controller::try_recv] approach is possible
|
||||||
/// * if neither is feasible a [Controller::poll]/[Controller::try_recv] approach is available
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait Controller<T : Sized + Send + Sync> : Sized + Send + Sync {
|
pub trait Controller<T : Sized + Send + Sync> : Sized + Send + Sync {
|
||||||
/// type of upstream values, used in [Self::send]
|
/// type of upstream values, used in [Self::send]
|
||||||
type Input;
|
type Input;
|
||||||
|
|
||||||
/// enqueue a new value to be sent
|
/// enqueue a new value to be sent to all other users
|
||||||
|
///
|
||||||
|
/// success or failure of this function does not imply validity of sent operation,
|
||||||
|
/// because it's integrated asynchronously on the background worker
|
||||||
fn send(&self, x: Self::Input) -> Result<()>;
|
fn send(&self, x: Self::Input) -> Result<()>;
|
||||||
|
|
||||||
/// get next value from stream, blocking until one is available
|
/// get next value from other users, blocking until one is available
|
||||||
///
|
///
|
||||||
/// this is just an async trait function wrapped by `async_trait`:
|
/// this is just an async trait function wrapped by `async_trait`:
|
||||||
///
|
///
|
||||||
|
@ -47,7 +49,7 @@ pub trait Controller<T : Sized + Send + Sync> : Sized + Send + Sync {
|
||||||
Ok(self.try_recv()?.expect("no message available after polling"))
|
Ok(self.try_recv()?.expect("no message available after polling"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// block until next value is added to the stream without removing any element
|
/// block until next value is available without consuming it
|
||||||
///
|
///
|
||||||
/// this is just an async trait function wrapped by `async_trait`:
|
/// this is just an async trait function wrapped by `async_trait`:
|
||||||
///
|
///
|
||||||
|
@ -55,9 +57,14 @@ pub trait Controller<T : Sized + Send + Sync> : Sized + Send + Sync {
|
||||||
async fn poll(&self) -> Result<()>;
|
async fn poll(&self) -> Result<()>;
|
||||||
|
|
||||||
/// attempt to receive a value without blocking, return None if nothing is available
|
/// attempt to receive a value without blocking, return None if nothing is available
|
||||||
|
///
|
||||||
|
/// note that this function does not circumvent race conditions, returning errors if it would
|
||||||
|
/// block. it's usually safe to ignore such errors and retry
|
||||||
fn try_recv(&self) -> Result<Option<T>>;
|
fn try_recv(&self) -> Result<Option<T>>;
|
||||||
|
|
||||||
/// sync variant of [Self::recv], blocking invoking thread
|
/// sync variant of [Self::recv], blocking invoking thread
|
||||||
|
/// this calls [Controller::recv] inside a [tokio::runtime::Runtime::block_on]
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
fn blocking_recv(&self, rt: &tokio::runtime::Handle) -> Result<T> {
|
fn blocking_recv(&self, rt: &tokio::runtime::Handle) -> Result<T> {
|
||||||
rt.block_on(self.recv())
|
rt.block_on(self.recv())
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,9 +25,6 @@ use crate::api::TextChange;
|
||||||
/// queues, transforming outbound delayed ops and applying remote changes
|
/// queues, transforming outbound delayed ops and applying remote changes
|
||||||
/// to the local buffer
|
/// to the local buffer
|
||||||
///
|
///
|
||||||
/// this controller implements [crate::api::OperationFactory], allowing to produce
|
|
||||||
/// Operation Sequences easily
|
|
||||||
///
|
|
||||||
/// upon dropping this handle will stop the associated worker
|
/// upon dropping this handle will stop the associated worker
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct BufferController {
|
pub struct BufferController {
|
||||||
|
|
|
@ -120,11 +120,8 @@ impl Client {
|
||||||
|
|
||||||
/// attach to a buffer, starting a buffer controller and returning a new reference to it
|
/// attach to a buffer, starting a buffer controller and returning a new reference to it
|
||||||
///
|
///
|
||||||
/// to interact with such buffer [crate::api::Controller::send] operation sequences
|
/// to interact with such buffer use [crate::api::Controller::send] or
|
||||||
/// or [crate::api::Controller::recv] for text events using its [crate::buffer::Controller].
|
/// [crate::api::Controller::recv] to exchange [crate::api::TextChange]
|
||||||
/// to generate operation sequences use the [crate::api::OperationFactory]
|
|
||||||
/// methods, which are implemented on [crate::buffer::Controller], such as
|
|
||||||
/// [crate::api::OperationFactory::diff].
|
|
||||||
pub async fn attach(&mut self, path: &str) -> Result<Arc<BufferController>, Error> {
|
pub async fn attach(&mut self, path: &str) -> Result<Arc<BufferController>, Error> {
|
||||||
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();
|
||||||
|
|
|
@ -26,13 +26,20 @@ impl From::<(i32, i32)> for RowCol {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RowCol {
|
||||||
|
/// create a RowCol and wrap into an Option, to help build protocol packets
|
||||||
|
pub fn wrap(row: i32, col: i32) -> Option<RowCol> {
|
||||||
|
Some(RowCol { row, col })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl CursorPosition {
|
impl CursorPosition {
|
||||||
/// extract start position, defaulting to (0,0)
|
/// extract start position, defaulting to (0,0), to help build protocol packets
|
||||||
pub fn start(&self) -> RowCol {
|
pub fn start(&self) -> RowCol {
|
||||||
self.start.clone().unwrap_or((0, 0).into())
|
self.start.clone().unwrap_or((0, 0).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// extract end position, defaulting to (0,0)
|
/// extract end position, defaulting to (0,0), to help build protocol packets
|
||||||
pub fn end(&self) -> RowCol {
|
pub fn end(&self) -> RowCol {
|
||||||
self.end.clone().unwrap_or((0, 0).into())
|
self.end.clone().unwrap_or((0, 0).into())
|
||||||
}
|
}
|
||||||
|
|
36
src/lib.rs
36
src/lib.rs
|
@ -14,17 +14,14 @@
|
||||||
//! Blocking and callback variants are also implemented. The [api::Controller] can also be used to send new
|
//! Blocking and callback variants are also implemented. The [api::Controller] can also be used to send new
|
||||||
//! events to the server ([api::Controller::send]).
|
//! events to the server ([api::Controller::send]).
|
||||||
//!
|
//!
|
||||||
//! Each operation on a buffer is represented as an [ot::OperationSeq].
|
//! Each operation on a buffer is represented as an [woot::crdt::Op]. The underlying buffer is a
|
||||||
//! A visualization about how OperationSeqs work is available
|
//! [WOOT CRDT](https://inria.hal.science/file/index/docid/71240/filename/RR-5580.pdf),
|
||||||
//! [here](http://operational-transformation.github.io/index.html),
|
//! but to use this library it's only sufficient to know that all WOOT buffers that have received
|
||||||
//! but to use this library it's only sufficient to know that they can only
|
//! the same operations converge to the same state, and that operations might not get integrated
|
||||||
//! be applied on buffers of some length and are transformable to be able to be
|
//! immediately but instead deferred until compatible.
|
||||||
//! applied in a different order while maintaining the same result.
|
|
||||||
//!
|
|
||||||
//! To generate Operation Sequences use helper methods from module [api::factory] (trait [api::OperationFactory]).
|
|
||||||
//!
|
//!
|
||||||
//! ## features
|
//! ## features
|
||||||
//! * `ot` : include the underlying operational transform library (default enabled)
|
//! * `woot` : include the underlying CRDT library and re-exports it (default enabled)
|
||||||
//! * `api` : include traits for core interfaces under [api] (default enabled)
|
//! * `api` : include traits for core interfaces under [api] (default enabled)
|
||||||
//! * `proto` : include GRCP protocol definitions under [proto] (default enabled)
|
//! * `proto` : include GRCP protocol definitions under [proto] (default enabled)
|
||||||
//! * `client`: include the local [client] implementation (default enabled)
|
//! * `client`: include the local [client] implementation (default enabled)
|
||||||
|
@ -40,7 +37,7 @@
|
||||||
//! [instance::a_sync::Instance]
|
//! [instance::a_sync::Instance]
|
||||||
//!
|
//!
|
||||||
//! ```rust,no_run
|
//! ```rust,no_run
|
||||||
//! use codemp::api::{Controller, OperationFactory};
|
//! use codemp::api::{Controller, TextChange};
|
||||||
//! # use codemp::instance::a_sync::Instance;
|
//! # use codemp::instance::a_sync::Instance;
|
||||||
//!
|
//!
|
||||||
//! # async fn async_example() -> codemp::Result<()> {
|
//! # async fn async_example() -> codemp::Result<()> {
|
||||||
|
@ -62,12 +59,9 @@
|
||||||
//! // attach to a new buffer and execute operations on it
|
//! // attach to a new buffer and execute operations on it
|
||||||
//! session.create("test.txt", None).await?; // create new buffer
|
//! session.create("test.txt", None).await?; // create new buffer
|
||||||
//! let buffer = session.attach("test.txt").await?; // attach to it
|
//! let buffer = session.attach("test.txt").await?; // attach to it
|
||||||
//! let text = buffer.content(); // any string can be used as operation factory
|
//! let local_change = TextChange { span: 0..0, content: "hello!".into() };
|
||||||
//! buffer.send(text.ins("hello", 0))?; // insert some text
|
//! buffer.send(local_change)?; // insert some text
|
||||||
//! if let Some(operation) = text.diff(4, "o world", 5) {
|
//! let remote_change = buffer.recv().await?;
|
||||||
//! buffer.send(operation)?; // replace with precision, if valid
|
|
||||||
//! }
|
|
||||||
//! assert_eq!(buffer.content(), "hello world");
|
|
||||||
//! #
|
//! #
|
||||||
//! # Ok(())
|
//! # Ok(())
|
||||||
//! # }
|
//! # }
|
||||||
|
@ -85,16 +79,6 @@
|
||||||
//! let session = Instance::default(); // instantiate sync variant
|
//! let session = Instance::default(); // instantiate sync variant
|
||||||
//! session.connect("http://alemi.dev:50051")?; // connect to server
|
//! session.connect("http://alemi.dev:50051")?; // connect to server
|
||||||
//!
|
//!
|
||||||
//! // join remote workspace and handle cursor events with a callback
|
|
||||||
//! let cursor = session.join("some_workspace")?; // join workspace
|
|
||||||
//! let (stop, stop_rx) = tokio::sync::mpsc::unbounded_channel(); // create stop channel
|
|
||||||
//! Arc::new(cursor).callback( // register callback
|
|
||||||
//! session.rt(), stop_rx, // pass instance runtime and stop channel receiver
|
|
||||||
//! | cursor_event | {
|
|
||||||
//! println!("received cursor event: {:?}", cursor_event);
|
|
||||||
//! }
|
|
||||||
//! );
|
|
||||||
//!
|
|
||||||
//! // attach to buffer and blockingly receive events
|
//! // attach to buffer and blockingly receive events
|
||||||
//! let buffer = session.attach("test.txt")?; // attach to buffer, must already exist
|
//! let buffer = session.attach("test.txt")?; // attach to buffer, must already exist
|
||||||
//! while let Ok(op) = buffer.blocking_recv(session.rt()) { // must pass runtime
|
//! while let Ok(op) = buffer.blocking_recv(session.rt()) { // must pass runtime
|
||||||
|
|
Loading…
Reference in a new issue