From 754b88fd736fb5f8d9c3a7e8e7fb6e3c48b24163 Mon Sep 17 00:00:00 2001 From: frelodev Date: Thu, 8 Aug 2024 19:54:47 +0200 Subject: [PATCH] feat: javascript glue Co-authored-by: alemi.dev --- src/client.rs | 1 + src/ffi/js/buffer.rs | 23 +++-------------- src/ffi/js/client.rs | 44 ++++++++++++++----------------- src/ffi/js/cursor.rs | 57 ++++++++--------------------------------- src/ffi/js/mod.rs | 18 +++++++------ src/ffi/js/workspace.rs | 8 +++--- 6 files changed, 47 insertions(+), 104 deletions(-) diff --git a/src/client.rs b/src/client.rs index f93356c..98c1f0c 100644 --- a/src/client.rs +++ b/src/client.rs @@ -52,6 +52,7 @@ impl AuthWrap { /// will disconnect when dropped /// can be used to interact with server #[derive(Debug, Clone)] +#[cfg_attr(feature = "js", napi_derive::napi)] pub struct Client(Arc); #[derive(Debug)] diff --git a/src/ffi/js/buffer.rs b/src/ffi/js/buffer.rs index c92da6f..cdf3b0c 100644 --- a/src/ffi/js/buffer.rs +++ b/src/ffi/js/buffer.rs @@ -1,27 +1,11 @@ -use std::sync::Arc; use napi::threadsafe_function::{ErrorStrategy::Fatal, ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode}; use napi_derive::napi; use crate::api::TextChange; -use crate::ffi::js::JsCodempError; use crate::api::Controller; use crate::prelude::*; - - -impl From for napi::Error { - fn from(value: crate::Error) -> Self { - let msg = format!("{value}"); - match value { - crate::Error::Deadlocked => napi::Error::new(napi::Status::WouldDeadlock, msg), - _ => napi::Error::new(napi::Status::GenericFailure, msg), - } - } -} - #[napi] impl CodempBufferController { - - #[napi(ts_args_type = "fun: (event: JsTextChange) => void")] pub fn callback(&self, fun: napi::JsFunction) -> napi::Result<()>{ let tsfn : ThreadsafeFunction = @@ -47,14 +31,13 @@ impl CodempBufferController { Ok(()) } - #[napi(js_name = "recv")] - pub async fn jsrecv(&self) -> napi::Result { + pub async fn js_recv(&self) -> napi::Result { Ok(self.recv().await?.into()) } - #[napi] - pub fn send(&self, op: TextChange) -> napi::Result<()> { + #[napi(js_name = "send")] + pub fn js_send(&self, op: TextChange) -> napi::Result<()> { Ok(self.send(op)?) } } \ No newline at end of file diff --git a/src/ffi/js/client.rs b/src/ffi/js/client.rs index de8446c..6a29e4e 100644 --- a/src/ffi/js/client.rs +++ b/src/ffi/js/client.rs @@ -1,43 +1,37 @@ use napi_derive::napi; -use crate::ffi::js::JsCodempError; - -#[napi] -/// main codemp client session -pub struct JsCodempClient(tokio::sync::RwLock); +use crate::prelude::*; #[napi] /// connect to codemp servers and return a client session -pub async fn connect(addr: Option) -> napi::Result{ - let client = crate::Client::new(addr.as_deref().unwrap_or("http://codemp.alemi.dev:50053")) +pub async fn connect(addr: Option, username: String, password: String) -> napi::Result{ + let client = crate::Client::new(addr.as_deref().unwrap_or("http://codemp.alemi.dev:50053"), username, password) .await?; - Ok(JsCodempClient(tokio::sync::RwLock::new(client))) + Ok(client) } #[napi] -impl JsCodempClient { - #[napi] - /// login against AuthService with provided credentials, optionally requesting access to a workspace - pub async fn login(&self, username: String, password: String, workspace_id: Option) -> napi::Result<()> { - self.0.read().await.login(username, password, workspace_id).await?; - Ok(()) - } - - #[napi] +impl CodempClient { + #[napi(js_name = "join_workspace")] /// join workspace with given id (will start its cursor controller) - pub async fn join_workspace(&self, workspace: String) -> napi::Result { - Ok(JsWorkspace::from(self.0.write().await.join_workspace(&workspace).await?)) + pub async fn js_join_workspace(&self, workspace: String) -> napi::Result { + Ok(self.join_workspace(workspace).await?) } - #[napi] + #[napi(js_name = "get_workspace")] /// get workspace with given id, if it exists - pub async fn get_workspace(&self, workspace: String) -> Option { - self.0.read().await.get_workspace(&workspace).map(|w| JsWorkspace::from(w)) + pub fn js_get_workspace(&self, workspace: String) -> Option { + self.get_workspace(&workspace) } - #[napi] + #[napi(js_name = "user_id")] /// return current sessions's user id - pub async fn user_id(&self) -> String { - self.0.read().await.user_id().to_string() + pub fn js_user_id(&self) -> String { + self.user_id().to_string() + } + + #[napi(js_name = "active_workspaces")] + pub fn js_active_workspaces(&self) -> Vec { + self.active_workspaces() } } \ No newline at end of file diff --git a/src/ffi/js/cursor.rs b/src/ffi/js/cursor.rs index 3c2c556..3a83daf 100644 --- a/src/ffi/js/cursor.rs +++ b/src/ffi/js/cursor.rs @@ -1,14 +1,10 @@ -use std::sync::Arc; use napi_derive::napi; -use uuid::Uuid; use napi::threadsafe_function::{ThreadsafeFunction, ThreadSafeCallContext, ThreadsafeFunctionCallMode, ErrorStrategy}; use crate::api::Controller; use crate::prelude::*; - - -#[napi(js_name = "Cursor")] +#[napi(object, js_name = "Cursor")] pub struct JsCursor { /// range of text change, as char indexes in buffer previous state pub start_row: i32, @@ -46,7 +42,6 @@ impl From for JsCursor { #[napi] impl CodempCursorController { - #[napi(ts_args_type = "fun: (event: JsCursorEvent) => void")] pub fn callback(&self, fun: napi::JsFunction) -> napi::Result<()>{ let tsfn : ThreadsafeFunction = @@ -70,46 +65,14 @@ impl CodempCursorController { Ok(()) } - #[napi] - pub fn send(&self, pos: &CodempCursorController) -> napi::Result<()> { - Ok(self.send(pos)?) + #[napi(js_name = "send")] + pub fn js_send(&self, pos: JsCursor) -> napi::Result<()> { + Ok(self.send(crate::api::Cursor::from(pos))?) + } + + + #[napi(js_name= "try_recv")] + pub fn js_try_recv(&self) -> napi::Result> { + Ok(self.try_recv()?.map(|x| JsCursor::from(x))) } } - - - -#[derive(Debug)] -#[napi(object)] -pub struct JsCursorEvent { - pub user: String, - pub buffer: String, - pub start: JsRowCol, - pub end: JsRowCol, -} - -impl From:: for JsCursorEvent { - fn from(value: codemp_proto::cursor::CursorEvent) -> Self { - let pos = value.position; - let start = pos.start; - let end = pos.end; - JsCursorEvent { - user: Uuid::from(value.user).to_string(), - buffer: pos.buffer.into(), - start: JsRowCol { row: start.row, col: start.col }, - end: JsRowCol { row: end.row, col: end.col }, - } - } -} - -#[derive(Debug)] -#[napi(object)] -pub struct JsRowCol { - pub row: i32, - pub col: i32 -} - -impl From:: for JsRowCol { - fn from(value: codemp_proto::cursor::RowCol) -> Self { - JsRowCol { row: value.row, col: value.col } - } -} \ No newline at end of file diff --git a/src/ffi/js/mod.rs b/src/ffi/js/mod.rs index cca5e30..ed23845 100644 --- a/src/ffi/js/mod.rs +++ b/src/ffi/js/mod.rs @@ -6,12 +6,13 @@ pub mod cursor; pub mod buffer; pub mod op_cache; -#[derive(Debug)] -struct JsCodempError(crate::Error); - -impl From:: for napi::Error { - fn from(value: JsCodempError) -> Self { - napi::Error::new(napi::Status::GenericFailure, &format!("CodempError: {:?}", value)) +impl From for napi::Error { + fn from(value: crate::Error) -> Self { + let msg = format!("{value}"); + match value { + crate::Error::Deadlocked => napi::Error::new(napi::Status::WouldDeadlock, msg), + _ => napi::Error::new(napi::Status::GenericFailure, msg), + } } } @@ -36,11 +37,12 @@ impl JsLogger { .with_line_number(false) .with_source_location(false) .compact(); - tracing_subscriber::fmt() + let _initialized = tracing_subscriber::fmt() .event_format(format) .with_max_level(level) .with_writer(std::sync::Mutex::new(JsLoggerProducer(tx))) - .init(); + .try_init() + .is_ok(); JsLogger(std::sync::Arc::new(tokio::sync::Mutex::new(rx))) } diff --git a/src/ffi/js/workspace.rs b/src/ffi/js/workspace.rs index b7571f7..188c4a4 100644 --- a/src/ffi/js/workspace.rs +++ b/src/ffi/js/workspace.rs @@ -36,9 +36,9 @@ impl CodempWorkspace { Ok(self.attach(&path).await?) } - /*#[napi] - pub async fn delete(&self, path: String) -> napi::Result<>{ - self.0.delete(&path) - }*/ + #[napi(js_name = "delete")] + pub async fn js_delete(&self, path: String) -> napi::Result<()> { + Ok(self.delete(&path).await?) + } } \ No newline at end of file