mirror of
https://github.com/hexedtech/codemp.git
synced 2024-12-22 21:04:53 +01:00
feat: moved out proto + feature flag cleanup
Co-authored-by: alemi <me@alemi.dev>
This commit is contained in:
parent
4694a01c9b
commit
a38c28f401
19 changed files with 28 additions and 335 deletions
35
Cargo.toml
35
Cargo.toml
|
@ -10,30 +10,19 @@ name = "codemp"
|
|||
# core
|
||||
tracing = "0.1"
|
||||
# woot
|
||||
codemp-woot = { git = "ssh://git@github.com/codewithotherpeopleandchangenamelater/woot.git", features = ["serde"], tag = "v0.1.2", optional = true }
|
||||
codemp-woot = { git = "ssh://git@github.com/codewithotherpeopleandchangenamelater/woot.git", features = ["serde"], tag = "v0.1.2" }
|
||||
# proto
|
||||
uuid = { version = "1.3.1", features = ["v4"], optional = true }
|
||||
tonic = { version = "0.9", features = ["tls", "tls-roots"], optional = true }
|
||||
prost = { version = "0.11.8", optional = true }
|
||||
codemp-proto = { git = "ssh://git@github.com/codewithotherpeopleandchangenamelater/codemp-proto.git", tag = "v0.6.1" }
|
||||
uuid = { version = "1.7", features = ["v4"] }
|
||||
tonic = { version = "0.11.0", features = ["tls", "tls-roots"] }
|
||||
# api
|
||||
similar = { version = "2.2", features = ["inline"], optional = true }
|
||||
tokio = { version = "1.0", features = ["macros", "rt-multi-thread", "sync"], optional = true }
|
||||
async-trait = { version = "0.1", optional = true }
|
||||
similar = { version = "2.2", features = ["inline"] }
|
||||
tokio = { version = "1.0", features = ["macros", "rt-multi-thread", "sync"] }
|
||||
async-trait = { version = "0.1" }
|
||||
# client
|
||||
md5 = { version = "0.7.0", optional = true }
|
||||
serde_json = { version = "1", optional = true }
|
||||
tokio-stream = { version = "0.1", optional = true }
|
||||
md5 = { version = "0.7.0" }
|
||||
serde_json = { version = "1" }
|
||||
tokio-stream = { version = "0.1" }
|
||||
serde = { version = "1.0.193", features = ["derive"] }
|
||||
dashmap = { version = "5.5.3", optional = true }
|
||||
postcard = { version = "1.0.8", optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
tonic-build = "0.9"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
api = ["dep:async-trait"]
|
||||
woot = ["dep:codemp-woot", "dep:similar"]
|
||||
proto = ["dep:prost", "dep:tonic", "dep:uuid"]
|
||||
client = ["proto", "api", "dep:tokio", "dep:tokio-stream", "dep:uuid", "dep:md5", "dep:serde_json", "dep:dashmap", "dep:postcard"]
|
||||
server = ["proto", "woot"]
|
||||
dashmap = { version = "5.5.3" }
|
||||
postcard = { version = "1.0.8" }
|
||||
|
|
18
build.rs
18
build.rs
|
@ -1,18 +0,0 @@
|
|||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
tonic_build::configure()
|
||||
// .build_client(cfg!(feature = "client"))
|
||||
// .build_server(cfg!(feature = "server")) // FIXME if false, build fails????
|
||||
// .build_transport(cfg!(feature = "proto"))
|
||||
.compile(
|
||||
&[
|
||||
"proto/common.proto",
|
||||
"proto/cursor.proto",
|
||||
"proto/files.proto",
|
||||
"proto/auth.proto",
|
||||
"proto/workspace.proto",
|
||||
"proto/buffer.proto",
|
||||
],
|
||||
&["proto"],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
syntax = "proto2";
|
||||
|
||||
package auth;
|
||||
|
||||
// authenticates users, issuing tokens
|
||||
service Auth {
|
||||
// send credentials and join a workspace, returns ready to use token
|
||||
rpc Login (WorkspaceJoinRequest) returns (Token);
|
||||
}
|
||||
|
||||
message Token {
|
||||
required string token = 1;
|
||||
}
|
||||
|
||||
// TODO one-request-to-do-it-all from login to workspace access
|
||||
message WorkspaceJoinRequest {
|
||||
required string username = 1;
|
||||
required string password = 2;
|
||||
optional string workspace_id = 3;
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
syntax = "proto2";
|
||||
|
||||
import "common.proto";
|
||||
|
||||
package buffer;
|
||||
|
||||
// handle buffer changes, keep in sync users
|
||||
service Buffer {
|
||||
// attach to a buffer and receive operations
|
||||
rpc Attach (stream Operation) returns (stream BufferEvent);
|
||||
}
|
||||
|
||||
message Operation {
|
||||
required bytes data = 1;
|
||||
}
|
||||
|
||||
message BufferEvent {
|
||||
required Operation op = 1;
|
||||
required common.Identity user = 2;
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
syntax = "proto2";
|
||||
|
||||
package common;
|
||||
|
||||
|
||||
// a wrapper payload representing an uuid
|
||||
message Identity {
|
||||
// uuid bytes, as string
|
||||
required string id = 1;
|
||||
}
|
||||
|
||||
// a collection of identities
|
||||
message IdentityList {
|
||||
repeated Identity users = 1;
|
||||
}
|
||||
|
||||
//generic Empty message
|
||||
message Empty { }
|
|
@ -1,36 +0,0 @@
|
|||
syntax = "proto2";
|
||||
|
||||
package cursor;
|
||||
import "common.proto";
|
||||
import "files.proto";
|
||||
|
||||
// handle cursor events and broadcast to all users
|
||||
service Cursor {
|
||||
// subscribe to a workspace's cursor events
|
||||
rpc Attach (stream cursor.CursorPosition) returns (stream cursor.CursorEvent);
|
||||
}
|
||||
|
||||
|
||||
// a tuple indicating row and column
|
||||
message RowCol {
|
||||
required int32 row = 1;
|
||||
required int32 col = 2;
|
||||
}
|
||||
|
||||
// cursor position object
|
||||
message CursorPosition {
|
||||
// path of current buffer this cursor is into
|
||||
required files.BufferNode buffer = 1;
|
||||
// cursor start position
|
||||
required RowCol start = 2;
|
||||
// cursor end position
|
||||
required RowCol end = 3;
|
||||
}
|
||||
|
||||
// cursor event, with user id and cursor position
|
||||
message CursorEvent {
|
||||
// user moving the cursor
|
||||
required common.Identity user = 1;
|
||||
// new cursor position
|
||||
required CursorPosition position = 2;
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
syntax = "proto2";
|
||||
|
||||
package files;
|
||||
|
||||
message BufferNode {
|
||||
required string path = 1;
|
||||
}
|
||||
|
||||
message BufferTree {
|
||||
repeated BufferNode buffers = 1;
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
syntax = "proto2";
|
||||
|
||||
package workspace;
|
||||
|
||||
import "common.proto";
|
||||
import "files.proto";
|
||||
import "auth.proto";
|
||||
|
||||
service Workspace {
|
||||
rpc Attach (common.Empty) returns (stream WorkspaceEvent);
|
||||
|
||||
rpc CreateBuffer (files.BufferNode) returns (common.Empty);
|
||||
rpc AccessBuffer (files.BufferNode) returns (BufferCredentials);
|
||||
rpc DeleteBuffer (files.BufferNode) returns (common.Empty);
|
||||
|
||||
rpc ListBuffers (common.Empty) returns (files.BufferTree);
|
||||
rpc ListUsers (common.Empty) returns (common.IdentityList);
|
||||
rpc ListBufferUsers (files.BufferNode) returns (common.IdentityList);
|
||||
}
|
||||
|
||||
message WorkspaceEvent {
|
||||
message UserJoin {
|
||||
required common.Identity user = 1;
|
||||
}
|
||||
message UserLeave {
|
||||
required common.Identity user = 1;
|
||||
}
|
||||
message FileCreate {
|
||||
required string path = 1;
|
||||
}
|
||||
message FileRename {
|
||||
required string before = 1;
|
||||
required string after = 2;
|
||||
}
|
||||
message FileDelete {
|
||||
required string path = 1;
|
||||
}
|
||||
|
||||
oneof event {
|
||||
UserJoin join = 1;
|
||||
UserLeave leave = 2;
|
||||
FileCreate create = 3;
|
||||
FileRename rename = 4;
|
||||
FileDelete delete = 5;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO this is very ugly because we can't just return a new token (which is already smelly but whatev), we also need to tell the underlying id so that
|
||||
// the client can put it as metadata while attaching, because it can't really know the underlying id that the server is using for each buffer without
|
||||
// parsing the token itself. meehhhhhh, this bleeds underlying implementation to the upper levels, how can we avoid this??
|
||||
message BufferCredentials {
|
||||
required common.Identity id = 1;
|
||||
required auth.Token token = 2;
|
||||
}
|
|
@ -3,7 +3,6 @@
|
|||
//! an editor-friendly representation of a text change in a buffer
|
||||
//! to easily interface with codemp from various editors
|
||||
|
||||
#[cfg(feature = "woot")]
|
||||
use crate::woot::{WootResult, woot::Woot, crdt::{TextEditor, CRDT, Op}};
|
||||
|
||||
/// an editor-friendly representation of a text change in a buffer
|
||||
|
@ -30,7 +29,6 @@ pub struct TextChange {
|
|||
}
|
||||
|
||||
impl TextChange {
|
||||
#[cfg(feature = "woot")]
|
||||
/// create a new TextChange from the difference of given strings
|
||||
pub fn from_diff(before: &str, after: &str) -> TextChange {
|
||||
let diff = similar::TextDiff::from_chars(before, after);
|
||||
|
@ -61,7 +59,6 @@ impl TextChange {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "woot")]
|
||||
/// consume the [TextChange], transforming it into a Vec of [woot::crdt::Op]
|
||||
pub fn transform(self, woot: &Woot) -> WootResult<Vec<Op>> {
|
||||
let mut out = Vec::new();
|
||||
|
@ -114,12 +111,11 @@ impl TextChange {
|
|||
|
||||
/// convert from byte index to row and column
|
||||
/// txt must be the whole content of the buffer, in order to count lines
|
||||
#[cfg(feature = "proto")]
|
||||
pub fn index_to_rowcol(txt: &str, index: usize) -> crate::proto::cursor::RowCol {
|
||||
pub fn index_to_rowcol(txt: &str, index: usize) -> codemp_proto::cursor::RowCol {
|
||||
// FIXME might panic, use .get()
|
||||
let row = txt[..index].matches('\n').count() as i32;
|
||||
let col = txt[..index].split('\n').last().unwrap_or("").len() as i32;
|
||||
crate::proto::cursor::RowCol { row, col }
|
||||
codemp_proto::cursor::RowCol { row, col }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ use woot::woot::Woot;
|
|||
use crate::errors::IgnorableError;
|
||||
use crate::api::controller::ControllerWorker;
|
||||
use crate::api::TextChange;
|
||||
use crate::proto::buffer::{BufferEvent, Operation};
|
||||
use codemp_proto::buffer::{BufferEvent, Operation};
|
||||
|
||||
use super::controller::BufferController;
|
||||
|
||||
|
|
|
@ -12,17 +12,17 @@ use tonic::transport::{Channel, Endpoint};
|
|||
use tonic::IntoRequest;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::proto::auth::auth_client::AuthClient;
|
||||
use codemp_proto::auth::auth_client::AuthClient;
|
||||
use codemp_proto::{
|
||||
common::Empty,
|
||||
buffer::buffer_client::BufferClient,
|
||||
cursor::cursor_client::CursorClient,
|
||||
auth::{Token, WorkspaceJoinRequest},
|
||||
workspace::workspace_client::WorkspaceClient,
|
||||
};
|
||||
use crate::{
|
||||
api::controller::ControllerWorker,
|
||||
cursor::worker::CursorWorker,
|
||||
proto::{
|
||||
common::Empty,
|
||||
buffer::buffer_client::BufferClient,
|
||||
cursor::cursor_client::CursorClient,
|
||||
auth::{Token, WorkspaceJoinRequest},
|
||||
workspace::workspace_client::WorkspaceClient,
|
||||
},
|
||||
workspace::Workspace
|
||||
};
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
use tokio::sync::{mpsc, broadcast::{self, error::{TryRecvError, RecvError}}, Mutex, watch};
|
||||
use tonic::async_trait;
|
||||
|
||||
use crate::{api::Controller, errors::IgnorableError, proto::cursor::{CursorEvent, CursorPosition}};
|
||||
|
||||
use crate::{api::Controller, errors::IgnorableError};
|
||||
use codemp_proto::cursor::{CursorEvent, CursorPosition};
|
||||
/// the cursor controller implementation
|
||||
///
|
||||
/// this contains
|
||||
|
|
|
@ -11,27 +11,3 @@ pub(crate) mod worker;
|
|||
pub mod controller;
|
||||
|
||||
pub use controller::CursorController as Controller;
|
||||
|
||||
use crate::proto::cursor::RowCol;
|
||||
|
||||
impl From::<RowCol> for (i32, i32) {
|
||||
fn from(pos: RowCol) -> (i32, i32) {
|
||||
(pos.row, pos.col)
|
||||
}
|
||||
}
|
||||
|
||||
impl From::<(i32, i32)> for RowCol {
|
||||
fn from((row, col): (i32, i32)) -> Self {
|
||||
RowCol { row, col }
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for RowCol {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
match self.row.partial_cmp(&other.row) {
|
||||
Some(core::cmp::Ordering::Equal) => {}
|
||||
ord => return ord,
|
||||
}
|
||||
self.col.partial_cmp(&other.col)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,8 @@ use std::sync::Arc;
|
|||
use tokio::sync::{mpsc, broadcast::{self}, Mutex, watch};
|
||||
use tonic::{Streaming, async_trait};
|
||||
|
||||
use crate::{api::controller::ControllerWorker, errors::IgnorableError, proto::cursor::{CursorPosition, CursorEvent}};
|
||||
use crate::{api::controller::ControllerWorker, errors::IgnorableError};
|
||||
use codemp_proto::cursor::{CursorPosition, CursorEvent};
|
||||
|
||||
use super::controller::CursorController;
|
||||
|
||||
|
|
|
@ -82,7 +82,6 @@ impl Display for Error {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
impl From<tonic::Status> for Error {
|
||||
fn from(status: tonic::Status) -> Self {
|
||||
Error::Transport {
|
||||
|
@ -92,7 +91,6 @@ impl From<tonic::Status> for Error {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
impl From<tonic::transport::Error> for Error {
|
||||
fn from(err: tonic::transport::Error) -> Self {
|
||||
Error::Transport {
|
||||
|
@ -102,28 +100,24 @@ impl From<tonic::transport::Error> for Error {
|
|||
}
|
||||
}
|
||||
|
||||
#[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 }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
impl<T> From<tokio::sync::watch::error::SendError<T>> for Error {
|
||||
fn from(_value: tokio::sync::watch::error::SendError<T>) -> Self {
|
||||
Error::Channel { send: true }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
impl From<tokio::sync::broadcast::error::RecvError> for Error {
|
||||
fn from(_value: tokio::sync::broadcast::error::RecvError) -> Self {
|
||||
Error::Channel { send: false }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
impl From<tokio::sync::watch::error::RecvError> for Error {
|
||||
fn from(_value: tokio::sync::watch::error::RecvError) -> Self {
|
||||
Error::Channel { send: false }
|
||||
|
|
|
@ -5,22 +5,18 @@
|
|||
//! the global instance reference is immutable and lazy-loaded, and requires `global` feature.
|
||||
|
||||
/// static global instance, allocated only if feature `global` is active
|
||||
#[cfg(feature = "global")]
|
||||
pub mod global {
|
||||
#[cfg(not(feature = "sync"))]
|
||||
lazy_static::lazy_static! {
|
||||
/// the global instance of codemp session
|
||||
pub static ref INSTANCE : super::a_sync::Instance = super::a_sync::Instance::default();
|
||||
}
|
||||
|
||||
#[cfg(feature = "sync")]
|
||||
lazy_static::lazy_static! {
|
||||
/// the global instance of codemp session
|
||||
pub static ref INSTANCE : super::sync::Instance = super::sync::Instance::default();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "global")]
|
||||
pub use global::INSTANCE;
|
||||
|
||||
/// async implementation of session instance
|
||||
|
|
72
src/lib.rs
72
src/lib.rs
|
@ -116,105 +116,33 @@
|
|||
|
||||
#![doc(html_no_source)]
|
||||
|
||||
|
||||
/// public traits exposed to clients
|
||||
#[cfg(feature = "api")]
|
||||
pub mod api;
|
||||
|
||||
/// cursor related types and controller
|
||||
#[cfg(feature = "client")]
|
||||
pub mod cursor;
|
||||
|
||||
/// buffer operations, factory, controller and types
|
||||
#[cfg(feature = "client")]
|
||||
pub mod buffer;
|
||||
|
||||
/// crate error types and helpers
|
||||
pub mod errors;
|
||||
|
||||
/// underlying client session manager
|
||||
#[cfg(feature = "client")]
|
||||
pub mod client;
|
||||
|
||||
/// workspace operations
|
||||
#[cfg(feature = "client")]
|
||||
pub mod workspace;
|
||||
|
||||
/// all-in-one imports : `use codemp::prelude::*;`
|
||||
pub mod prelude;
|
||||
|
||||
/// underlying OperationalTransform library used, re-exported
|
||||
#[cfg(feature = "woot")]
|
||||
pub use woot;
|
||||
|
||||
/// protocol types and services auto-generated by grpc
|
||||
#[cfg(feature = "proto")]
|
||||
#[allow(non_snake_case)]
|
||||
pub mod proto {
|
||||
pub mod common {
|
||||
tonic::include_proto!("common");
|
||||
|
||||
impl From<uuid::Uuid> for Identity {
|
||||
fn from(id: uuid::Uuid) -> Self {
|
||||
Identity { id: id.to_string() }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&uuid::Uuid> for Identity {
|
||||
fn from(id: &uuid::Uuid) -> Self {
|
||||
Identity { id: id.to_string() }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Identity> for uuid::Uuid {
|
||||
fn from(value: Identity) -> Self {
|
||||
uuid::Uuid::parse_str(&value.id).expect("invalid uuid in identity")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Identity> for uuid::Uuid {
|
||||
fn from(value: &Identity) -> Self {
|
||||
uuid::Uuid::parse_str(&value.id).expect("invalid uuid in identity")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub mod files {
|
||||
tonic::include_proto!("files");
|
||||
|
||||
impl From<String> for BufferNode {
|
||||
fn from(value: String) -> Self {
|
||||
BufferNode { path: value }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for BufferNode {
|
||||
fn from(value: &str) -> Self {
|
||||
BufferNode { path: value.to_string() }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BufferNode> for String {
|
||||
fn from(value: BufferNode) -> Self {
|
||||
value.path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod buffer { tonic::include_proto!("buffer"); }
|
||||
pub mod cursor { tonic::include_proto!("cursor"); }
|
||||
pub mod workspace { tonic::include_proto!("workspace"); }
|
||||
pub mod auth { tonic::include_proto!("auth"); }
|
||||
}
|
||||
|
||||
pub use errors::Error;
|
||||
pub use errors::Result;
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
pub use client::Client;
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
pub use workspace::Workspace;
|
||||
|
||||
|
||||
|
|
|
@ -7,16 +7,13 @@ pub use crate::{
|
|||
Result as CodempResult,
|
||||
};
|
||||
|
||||
#[cfg(feature = "woot")]
|
||||
pub use crate::woot::crdt::Op as CodempOp;
|
||||
|
||||
#[cfg(feature = "api")]
|
||||
pub use crate::api::{
|
||||
Controller as CodempController,
|
||||
TextChange as CodempTextChange,
|
||||
};
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
pub use crate::{
|
||||
// Instance as CodempInstance,
|
||||
client::Client as CodempClient,
|
||||
|
@ -25,10 +22,3 @@ pub use crate::{
|
|||
cursor::Controller as CodempCursorController,
|
||||
buffer::Controller as CodempBufferController,
|
||||
};
|
||||
|
||||
#[cfg(feature = "proto")]
|
||||
pub use crate::{
|
||||
proto::cursor::CursorPosition as CodempCursorPosition,
|
||||
proto::cursor::CursorEvent as CodempCursorEvent,
|
||||
proto::cursor::RowCol as CodempRowCol,
|
||||
};
|
||||
|
|
|
@ -5,8 +5,8 @@ use tonic::Streaming;
|
|||
use uuid::Uuid;
|
||||
use crate::{
|
||||
api::controller::ControllerWorker, buffer::{self, worker::BufferWorker}, client::Services, cursor,
|
||||
proto::{auth::Token, common::{Identity, Empty}, files::BufferNode, workspace::{WorkspaceEvent, workspace_event::{Event as WorkspaceEventInner, FileCreate, FileDelete, FileRename, UserJoin, UserLeave}}}
|
||||
};
|
||||
use codemp_proto::{auth::Token, common::{Identity, Empty}, files::BufferNode, workspace::{WorkspaceEvent, workspace_event::{Event as WorkspaceEventInner, FileCreate, FileDelete, FileRename, UserJoin, UserLeave}}};
|
||||
|
||||
//TODO may contain more info in the future
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
Loading…
Reference in a new issue