mirror of
https://github.com/hexedtech/codemp.git
synced 2024-11-26 00:44:48 +01:00
Merge remote-tracking branch 'origin/glue' into glue
This commit is contained in:
commit
df95b20728
7 changed files with 53 additions and 64 deletions
11
Cargo.toml
11
Cargo.toml
|
@ -10,6 +10,7 @@ crate-type = ["cdylib"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# core
|
# core
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
|
thiserror = { version = "1.0.57" }
|
||||||
# woot
|
# woot
|
||||||
codemp-woot = { git = "ssh://git@github.com/hexedtech/woot.git", features = ["serde"], tag = "v0.1.2" }
|
codemp-woot = { git = "ssh://git@github.com/hexedtech/woot.git", features = ["serde"], tag = "v0.1.2" }
|
||||||
# proto
|
# proto
|
||||||
|
@ -34,13 +35,9 @@ tracing-subscriber = { version = "0.3.18", optional = true }
|
||||||
|
|
||||||
# glue (java)
|
# glue (java)
|
||||||
jni = { version = "0.21.1", features = ["invocation"], optional = true }
|
jni = { version = "0.21.1", features = ["invocation"], optional = true }
|
||||||
jni-sys = { version = "0.3.0", optional = true }
|
#jni-sys = { version = "0.3.0", optional = true }
|
||||||
rifgen = { git = "https://github.com/Kofituo/rifgen.git", rev = "d27d9785b2febcf5527f1deb6a846be5d583f7d7", optional = true }
|
|
||||||
log = { version = "0.4.21", optional = true }
|
|
||||||
|
|
||||||
# glue (lua)
|
# glue (lua)
|
||||||
mlua = { version = "0.9.6", features = ["module", "luajit", "send"], optional = true }
|
mlua = { version = "0.9.6", features = ["module", "luajit", "send"], optional = true }
|
||||||
thiserror = { version = "1.0.57", optional = true }
|
|
||||||
derive_more = { version = "0.99.17", optional = true }
|
derive_more = { version = "0.99.17", optional = true }
|
||||||
|
|
||||||
# glue (js)
|
# glue (js)
|
||||||
|
@ -64,8 +61,8 @@ pyo3-build-config = { version = "0.19.2", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
lua = ["mlua", "thiserror", "derive_more", "lazy_static", "tracing-subscriber"]
|
lua = ["mlua", "derive_more", "lazy_static", "tracing-subscriber"]
|
||||||
java = ["lazy_static", "jni", "jni-sys", "flapigen", "rifgen", "log"]
|
java = ["lazy_static", "jni", "tracing-subscriber"]
|
||||||
java-artifact = ["java"] # also builds the jar
|
java-artifact = ["java"] # also builds the jar
|
||||||
js = ["napi-build", "tracing-subscriber", "rmpv", "napi", "napi-derive", "futures"]
|
js = ["napi-build", "tracing-subscriber", "rmpv", "napi", "napi-derive", "futures"]
|
||||||
python = ["pyo3", "pyo3-asyncio", "tracing-subscriber", "pyo3-build-config"]
|
python = ["pyo3", "pyo3-asyncio", "tracing-subscriber", "pyo3-build-config"]
|
||||||
|
|
|
@ -25,14 +25,11 @@ pub(crate) trait ControllerWorker<T : Sized + Send + Sync> {
|
||||||
/// * if async is not feasible a [Controller::poll]/[Controller::try_recv] approach is possible
|
/// * if async is not feasible a [Controller::poll]/[Controller::try_recv] approach is possible
|
||||||
#[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 Input;
|
|
||||||
|
|
||||||
/// enqueue a new value to be sent to all other users
|
/// enqueue a new value to be sent to all other users
|
||||||
///
|
///
|
||||||
/// success or failure of this function does not imply validity of sent operation,
|
/// success or failure of this function does not imply validity of sent operation,
|
||||||
/// because it's integrated asynchronously on the background worker
|
/// because it's integrated asynchronously on the background worker
|
||||||
fn send(&self, x: Self::Input) -> Result<()>;
|
fn send(&self, x: T) -> Result<()>;
|
||||||
|
|
||||||
/// get next value from other users, blocking until one is available
|
/// get next value from other users, blocking until one is available
|
||||||
///
|
///
|
||||||
|
|
|
@ -77,8 +77,6 @@ impl Drop for StopOnDrop {
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Controller<TextChange> for BufferController {
|
impl Controller<TextChange> for BufferController {
|
||||||
type Input = TextChange;
|
|
||||||
|
|
||||||
/// block until a text change is available
|
/// block until a text change is available
|
||||||
/// this returns immediately if one is already available
|
/// this returns immediately if one is already available
|
||||||
async fn poll(&self) -> crate::Result<()> {
|
async fn poll(&self) -> crate::Result<()> {
|
||||||
|
|
|
@ -67,8 +67,6 @@ impl CursorController {
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Controller<Cursor> for CursorController {
|
impl Controller<Cursor> for CursorController {
|
||||||
type Input = Cursor;
|
|
||||||
|
|
||||||
/// enqueue a cursor event to be broadcast to current workspace
|
/// enqueue a cursor event to be broadcast to current workspace
|
||||||
/// will automatically invert cursor start/end if they are inverted
|
/// will automatically invert cursor start/end if they are inverted
|
||||||
fn send(&self, mut cursor: Cursor) -> crate::Result<()> {
|
fn send(&self, mut cursor: Cursor) -> crate::Result<()> {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
//!
|
//!
|
||||||
//! library error helpers and types
|
//! library error helpers and types
|
||||||
|
|
||||||
use std::{result::Result as StdResult, error::Error as StdError, fmt::Display};
|
use std::result::Result as StdResult;
|
||||||
|
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
|
@ -45,41 +45,30 @@ pub type Result<T> = StdResult<T, Error>;
|
||||||
|
|
||||||
// TODO split this into specific errors for various parts of the library
|
// TODO split this into specific errors for various parts of the library
|
||||||
/// codemp error type for library issues
|
/// codemp error type for library issues
|
||||||
#[derive(Debug)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// errors caused by tonic http layer
|
/// errors caused by tonic http layer
|
||||||
|
#[error("tonic error (status: {status}, message: {message})")]
|
||||||
Transport {
|
Transport {
|
||||||
status: String,
|
status: String,
|
||||||
message: String,
|
message: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// errors caused by async channels
|
/// errors caused by async channels
|
||||||
|
#[error("channel error, send: {send}")]
|
||||||
Channel {
|
Channel {
|
||||||
send: bool
|
send: bool
|
||||||
},
|
},
|
||||||
|
|
||||||
/// errors caused by wrong usage of library objects
|
/// errors caused by wrong usage of library objects
|
||||||
|
#[error("invalid state error: {msg}")]
|
||||||
InvalidState {
|
InvalidState {
|
||||||
msg: String,
|
msg: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// errors caused by wrong interlocking, safe to retry
|
/// errors caused by wrong interlocking, safe to retry
|
||||||
Deadlocked,
|
#[error("deadlocked error")]
|
||||||
|
Deadlocked
|
||||||
/// if you see these errors someone is being lazy (:
|
|
||||||
Filler { // TODO filler error, remove later
|
|
||||||
message: String,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StdError for Error {}
|
|
||||||
|
|
||||||
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),
|
|
||||||
Self::Channel { send } => write!(f, "Channel error (send:{})", send),
|
|
||||||
_ => write!(f, "Unknown error"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<tonic::Status> for Error {
|
impl From<tonic::Status> for Error {
|
||||||
|
|
|
@ -1,14 +1,18 @@
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::sync::{mpsc, Arc, Mutex};
|
use std::sync::atomic::AtomicBool;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use crate::api::Cursor;
|
use crate::api::Cursor;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use mlua::prelude::*;
|
use mlua::prelude::*;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
|
use tokio::sync::broadcast;
|
||||||
|
|
||||||
lazy_static::lazy_static!{
|
lazy_static::lazy_static!{
|
||||||
// TODO use a runtime::Builder::new_current_thread() runtime to not behave like malware
|
// TODO use a runtime::Builder::new_current_thread() runtime to not behave like malware
|
||||||
static ref STATE : GlobalState = GlobalState::default();
|
static ref STATE : GlobalState = GlobalState::default();
|
||||||
|
static ref LOG : broadcast::Sender<String> = broadcast::channel(32).0;
|
||||||
|
static ref ONCE : AtomicBool = AtomicBool::new(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GlobalState {
|
struct GlobalState {
|
||||||
|
@ -19,8 +23,9 @@ struct GlobalState {
|
||||||
impl Default for GlobalState {
|
impl Default for GlobalState {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let rt = Runtime::new().expect("could not create tokio runtime");
|
let rt = Runtime::new().expect("could not create tokio runtime");
|
||||||
|
let addr = std::env::var("CODEMP_SERVER_ADDRESS").unwrap_or_else(|_|"http://codemp.alemi.dev:50053".to_string());
|
||||||
let client = rt.block_on(
|
let client = rt.block_on(
|
||||||
CodempClient::new("http://codemp.alemi.dev:50053")
|
CodempClient::new(&addr)
|
||||||
).expect("could not connect to codemp servers");
|
).expect("could not connect to codemp servers");
|
||||||
GlobalState { client: std::sync::RwLock::new(client), runtime: rt }
|
GlobalState { client: std::sync::RwLock::new(client), runtime: rt }
|
||||||
}
|
}
|
||||||
|
@ -198,36 +203,29 @@ impl LuaUserData for CodempTextChange {
|
||||||
|
|
||||||
// setup library logging to file
|
// setup library logging to file
|
||||||
#[derive(Debug, derive_more::From)]
|
#[derive(Debug, derive_more::From)]
|
||||||
struct LuaLogger(Arc<Mutex<mpsc::Receiver<String>>>);
|
struct LuaLogger(broadcast::Receiver<String>);
|
||||||
impl LuaUserData for LuaLogger {
|
impl LuaUserData for LuaLogger {
|
||||||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
methods.add_method("recv", |_, this, ()| {
|
methods.add_method_mut("recv", |_, this, ()| {
|
||||||
Ok(
|
Ok(this.0.blocking_recv().expect("logger channel closed"))
|
||||||
this.0
|
|
||||||
.lock()
|
|
||||||
.expect("logger mutex poisoned")
|
|
||||||
.recv()
|
|
||||||
.expect("logger channel closed")
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct LuaLoggerProducer(mpsc::Sender<String>);
|
struct LuaLoggerProducer;
|
||||||
impl Write for LuaLoggerProducer {
|
impl Write for LuaLoggerProducer {
|
||||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||||
self.0.send(String::from_utf8_lossy(buf).to_string())
|
let _ = LOG.send(String::from_utf8_lossy(buf).to_string());
|
||||||
.expect("could not write on logger channel");
|
|
||||||
Ok(buf.len())
|
Ok(buf.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&mut self) -> std::io::Result<()> { Ok(()) }
|
fn flush(&mut self) -> std::io::Result<()> { Ok(()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_tracing(_: &Lua, (debug,): (Option<bool>,)) -> LuaResult<LuaLogger> {
|
fn setup_logger(_: &Lua, (debug, path): (Option<bool>, Option<String>)) -> LuaResult<()> {
|
||||||
let (tx, rx) = mpsc::channel();
|
if ONCE.load(std::sync::atomic::Ordering::Relaxed) { return Ok(()) }
|
||||||
let level = if debug.unwrap_or(false) { tracing::Level::DEBUG } else {tracing::Level::INFO };
|
|
||||||
let format = tracing_subscriber::fmt::format()
|
let format = tracing_subscriber::fmt::format()
|
||||||
.with_level(true)
|
.with_level(true)
|
||||||
.with_target(true)
|
.with_target(true)
|
||||||
|
@ -238,17 +236,32 @@ fn setup_tracing(_: &Lua, (debug,): (Option<bool>,)) -> LuaResult<LuaLogger> {
|
||||||
.with_line_number(false)
|
.with_line_number(false)
|
||||||
.with_source_location(false)
|
.with_source_location(false)
|
||||||
.compact();
|
.compact();
|
||||||
tracing_subscriber::fmt()
|
|
||||||
|
let level = if debug.unwrap_or_default() { tracing::Level::DEBUG } else {tracing::Level::INFO };
|
||||||
|
|
||||||
|
let builder = tracing_subscriber::fmt()
|
||||||
.event_format(format)
|
.event_format(format)
|
||||||
.with_max_level(level)
|
.with_max_level(level);
|
||||||
.with_writer(Mutex::new(LuaLoggerProducer(tx)))
|
|
||||||
.init();
|
if let Some(path) = path {
|
||||||
Ok(LuaLogger(Arc::new(Mutex::new(rx))))
|
let logfile = std::fs::File::create(path).expect("failed creating logfile");
|
||||||
|
builder.with_writer(Mutex::new(logfile)).init();
|
||||||
|
} else {
|
||||||
|
builder.with_writer(Mutex::new(LuaLoggerProducer)).init();
|
||||||
|
}
|
||||||
|
|
||||||
|
ONCE.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_logger(_: &Lua, (): ()) -> LuaResult<LuaLogger> {
|
||||||
|
let sub = LOG.subscribe();
|
||||||
|
Ok(LuaLogger(sub))
|
||||||
}
|
}
|
||||||
|
|
||||||
// define module and exports
|
// define module and exports
|
||||||
#[mlua::lua_module]
|
#[mlua::lua_module]
|
||||||
fn libcodemp(lua: &Lua) -> LuaResult<LuaTable> {
|
fn codemp_lua(lua: &Lua) -> LuaResult<LuaTable> {
|
||||||
let exports = lua.create_table()?;
|
let exports = lua.create_table()?;
|
||||||
|
|
||||||
// core proto functions
|
// core proto functions
|
||||||
|
@ -258,9 +271,9 @@ fn libcodemp(lua: &Lua) -> LuaResult<LuaTable> {
|
||||||
exports.set("get_workspace", lua.create_function(get_workspace)?)?;
|
exports.set("get_workspace", lua.create_function(get_workspace)?)?;
|
||||||
// debug
|
// debug
|
||||||
exports.set("id", lua.create_function(id)?)?;
|
exports.set("id", lua.create_function(id)?)?;
|
||||||
exports.set("setup_tracing", lua.create_function(setup_tracing)?)?;
|
exports.set("get_logger", lua.create_function(get_logger)?)?;
|
||||||
|
exports.set("setup_logger", lua.create_function(setup_logger)?)?;
|
||||||
|
|
||||||
Ok(exports)
|
Ok(exports)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -25,10 +25,7 @@ impl From<CodempError> for PyErr {
|
||||||
CodempError::InvalidState { msg } => {
|
CodempError::InvalidState { msg } => {
|
||||||
PyRuntimeError::new_err(format!("Invalid state: {}", msg))
|
PyRuntimeError::new_err(format!("Invalid state: {}", msg))
|
||||||
}
|
}
|
||||||
CodempError::Deadlocked => PyRuntimeError::new_err(format!("Deadlock, retry.")),
|
CodempError::Deadlocked => PyRuntimeError::new_err(format!("Deadlock, retry."))
|
||||||
CodempError::Filler { message } => {
|
|
||||||
PyBaseException::new_err(format!("Generic error: {}", message))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue