chore: modularized crate into features

by default "client" is on so that it works like before but it's possible
to cherry pick features out and (for example) only build the grpc proto
structs
This commit is contained in:
əlemi 2023-09-10 03:40:31 +02:00
parent c0892b36cd
commit 4ec99bab36
5 changed files with 76 additions and 45 deletions

View file

@ -9,24 +9,31 @@ name = "codemp"
[dependencies] [dependencies]
# core # core
tracing = "0.1" tracing = "0.1"
tonic = { version = "0.9", features = ["tls", "tls-roots"] } # ot
operational-transform = { version = "0.6", features = ["serde"], optional = true }
# proto
tonic = { version = "0.9", features = ["tls", "tls-roots"], optional = true }
prost = { version = "0.11.8", optional = true } prost = { version = "0.11.8", optional = true }
md5 = "0.7.0" # api
uuid = { version = "1.3.1", features = ["v4"] } similar = { version = "2.2", features = ["inline"], optional = true }
operational-transform = { version = "0.6", features = ["serde"] } tokio = { version = "1.0", features = ["macros", "rt-multi-thread", "sync", "full"], optional = true }
tokio = { version = "1.0", features = ["macros", "rt-multi-thread", "sync", "full"], optional = false } async-trait = { version = "0.1", optional = true }
tokio-stream = { version = "0.1", optional = false } # client
serde = { version = "1", optional = false } md5 = { version = "0.7.0", optional = true }
serde_json = { version = "1", optional = false } uuid = { version = "1.3.1", features = ["v4"], optional = true }
tracing-subscriber = { version = "0.3", optional = true } serde_json = { version = "1", optional = true }
similar = { version = "2.2", features = ["inline"] } tokio-stream = { version = "0.1", optional = true }
# global
lazy_static = { version = "1.4", optional = true } lazy_static = { version = "1.4", optional = true }
[build-dependencies] [build-dependencies]
tonic-build = "0.9" tonic-build = "0.9"
[features] [features]
default = ["proto"] default = ["client"]
proto = ["dep:prost"] api = ["ot", "dep:similar", "dep:tokio", "dep:async-trait"]
global = ["dep:lazy_static"] ot = ["dep:operational-transform"]
sync = [] proto = ["dep:prost", "dep:tonic"]
client = ["proto", "api", "dep:tokio", "dep:tokio-stream", "dep:uuid", "dep:md5", "dep:serde_json"]
global = ["client", "dep:lazy_static"]
sync = ["client"]

View file

@ -7,7 +7,7 @@ use crate::Result;
use std::sync::Arc; use std::sync::Arc;
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
#[tonic::async_trait] #[async_trait::async_trait]
pub(crate) trait ControllerWorker<T : Sized + Send + Sync> { pub(crate) trait ControllerWorker<T : Sized + Send + Sync> {
type Controller : Controller<T>; type Controller : Controller<T>;
type Tx; type Tx;
@ -27,7 +27,7 @@ pub(crate) trait ControllerWorker<T : Sized + Send + Sync> {
/// * if possible, prefer a pure [Controller::recv] consumer /// * if possible, prefer a pure [Controller::recv] consumer
/// * a second possibility in preference is using a [Controller::callback] /// * a second possibility in preference is using a [Controller::callback]
/// * if neither is feasible a [Controller::poll]/[Controller::try_recv] approach is available /// * if neither is feasible a [Controller::poll]/[Controller::try_recv] approach is available
#[tonic::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;

View file

@ -4,8 +4,6 @@
use std::{result::Result as StdResult, error::Error as StdError, fmt::Display}; use std::{result::Result as StdResult, error::Error as StdError, fmt::Display};
use tokio::sync::{mpsc, broadcast, watch};
use tonic::{Status, Code};
use tracing::warn; use tracing::warn;
/// an error which can be ignored with just a warning entry /// an error which can be ignored with just a warning entry
@ -51,7 +49,7 @@ pub type Result<T> = StdResult<T, Error>;
pub enum Error { pub enum Error {
/// errors caused by tonic http layer /// errors caused by tonic http layer
Transport { Transport {
status: Code, status: String,
message: String, message: String,
}, },
/// errors caused by async channels /// errors caused by async channels
@ -81,34 +79,43 @@ impl Display for Error {
} }
} }
impl From<Status> for Error { #[cfg(feature = "client")]
fn from(status: Status) -> Self { impl From<tonic::Status> for Error {
Error::Transport { status: status.code(), message: status.message().to_string() } fn from(status: tonic::Status) -> Self {
}
}
impl From<tonic::transport::Error> for Error {
fn from(err: tonic::transport::Error) -> Self {
Error::Transport { Error::Transport {
status: Code::Unknown, message: format!("underlying transport error: {:?}", err) status: status.code().to_string(),
message: status.message().to_string()
} }
} }
} }
impl<T> From<mpsc::error::SendError<T>> for Error { #[cfg(feature = "client")]
fn from(_value: mpsc::error::SendError<T>) -> Self { impl From<tonic::transport::Error> for Error {
fn from(err: tonic::transport::Error) -> Self {
Error::Transport {
status: tonic::Code::Unknown.to_string(),
message: format!("underlying transport error: {:?}", err)
}
}
}
#[cfg(feature = "client")]
impl<T> From<tokio::sync::mpsc::error::SendError<T>> for Error {
fn from(_value: tokio::sync::mpsc::error::SendError<T>) -> Self {
Error::Channel { send: true } Error::Channel { send: true }
} }
} }
impl From<broadcast::error::RecvError> for Error { #[cfg(feature = "client")]
fn from(_value: broadcast::error::RecvError) -> Self { impl From<tokio::sync::broadcast::error::RecvError> for Error {
fn from(_value: tokio::sync::broadcast::error::RecvError) -> Self {
Error::Channel { send: false } Error::Channel { send: false }
} }
} }
impl From<watch::error::RecvError> for Error { #[cfg(feature = "client")]
fn from(_value: watch::error::RecvError) -> Self { impl From<tokio::sync::watch::error::RecvError> for Error {
fn from(_value: tokio::sync::watch::error::RecvError) -> Self {
Error::Channel { send: false } Error::Channel { send: false }
} }
} }

View file

@ -143,27 +143,33 @@
/// public traits exposed to clients /// public traits exposed to clients
#[cfg(feature = "api")]
pub mod api; pub mod api;
/// cursor related types and controller /// cursor related types and controller
#[cfg(feature = "client")]
pub mod cursor; pub mod cursor;
/// buffer operations, factory, controller and types /// buffer operations, factory, controller and types
#[cfg(feature = "client")]
pub mod buffer; pub mod buffer;
/// crate error types and helpers /// crate error types and helpers
pub mod errors; pub mod errors;
/// underlying client session manager /// underlying client session manager
#[cfg(feature = "client")]
pub mod client; pub mod client;
/// client wrapper to handle memory persistence /// client wrapper to handle memory persistence
#[cfg(feature = "client")]
pub mod instance; pub mod instance;
/// all-in-one imports : `use codemp::prelude::*;` /// all-in-one imports : `use codemp::prelude::*;`
pub mod prelude; pub mod prelude;
/// underlying OperationalTransform library used, re-exported /// underlying OperationalTransform library used, re-exported
#[cfg(feature = "ot")]
pub use operational_transform as ot; pub use operational_transform as ot;
/// protocol types and services auto-generated by grpc /// protocol types and services auto-generated by grpc
@ -175,11 +181,12 @@ pub mod proto {
} }
pub use api::Controller;
pub use client::Client;
pub use errors::Error; pub use errors::Error;
pub use errors::Result; pub use errors::Result;
#[cfg(feature = "sync")] pub use instance::sync::Instance;
#[cfg(not(feature = "sync"))] pub use instance::a_sync::Instance; #[cfg(all(feature = "client", feature = "sync"))]
pub use instance::sync::Instance;
#[cfg(all(feature = "client", not(feature = "sync")))]
pub use instance::a_sync::Instance;

View file

@ -5,21 +5,31 @@
pub use crate::{ pub use crate::{
Error as CodempError, Error as CodempError,
Result as CodempResult, Result as CodempResult,
};
Client as CodempClient,
#[cfg(feature = "ot")]
pub use crate::ot::OperationSeq as CodempOperationSeq;
#[cfg(feature = "api")]
pub use crate::{
api::Controller as CodempController, api::Controller as CodempController,
api::OperationFactory as CodempOperationFactory, api::OperationFactory as CodempOperationFactory,
};
#[cfg(feature = "client")]
pub use crate::{
client::Client as CodempClient,
cursor::Controller as CodempCursorController, cursor::Controller as CodempCursorController,
buffer::Controller as CodempBufferController, buffer::Controller as CodempBufferController,
ot::OperationSeq as CodempOperationSeq,
buffer::TextChange as CodempTextChange, buffer::TextChange as CodempTextChange,
Instance as CodempInstance,
};
#[cfg(feature = "proto")]
pub use crate::{
proto::CursorPosition as CodempCursorPosition, proto::CursorPosition as CodempCursorPosition,
proto::CursorEvent as CodempCursorEvent, proto::CursorEvent as CodempCursorEvent,
proto::RowCol as CodempRowCol, proto::RowCol as CodempRowCol,
Instance as CodempInstance,
}; };
#[cfg(feature = "global")] #[cfg(feature = "global")]