mirror of
https://github.com/hexedtech/codemp.git
synced 2024-11-21 23:04:49 +01:00
Merge pull request #18 from hexedtech/feat/jni-toolbox
feat: use jni-toolbox for glue
This commit is contained in:
commit
3f2f98a9f2
8 changed files with 299 additions and 781 deletions
23
Cargo.lock
generated
23
Cargo.lock
generated
|
@ -238,6 +238,7 @@ dependencies = [
|
||||||
"dashmap",
|
"dashmap",
|
||||||
"diamond-types",
|
"diamond-types",
|
||||||
"jni",
|
"jni",
|
||||||
|
"jni-toolbox",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"mlua-codemp-patch",
|
"mlua-codemp-patch",
|
||||||
"napi",
|
"napi",
|
||||||
|
@ -746,6 +747,28 @@ version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jni-toolbox"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eeae2881d819e208fcfceea81eb5a8ca6c131c6fb1605dfe2f3a31dea061ec7c"
|
||||||
|
dependencies = [
|
||||||
|
"jni",
|
||||||
|
"jni-toolbox-macro",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jni-toolbox-macro"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e480850db18f0cc95120e7bf86af772c31b3c0f0dd3d3600682d8bd8399f4ae"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.77",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.70"
|
version = "0.3.70"
|
||||||
|
|
|
@ -41,6 +41,7 @@ tracing-subscriber = { version = "0.3", optional = true }
|
||||||
# glue (java)
|
# glue (java)
|
||||||
lazy_static = { version = "1.5", optional = true }
|
lazy_static = { version = "1.5", optional = true }
|
||||||
jni = { version = "0.21", features = ["invocation"], optional = true }
|
jni = { version = "0.21", features = ["invocation"], optional = true }
|
||||||
|
jni-toolbox = { version = "0.2.0", optional = true, features = ["uuid"] }
|
||||||
|
|
||||||
# glue (lua)
|
# glue (lua)
|
||||||
mlua-codemp-patch = { version = "0.10.0-beta.2", features = ["module", "send", "serialize"], optional = true }
|
mlua-codemp-patch = { version = "0.10.0-beta.2", features = ["module", "send", "serialize"], optional = true }
|
||||||
|
@ -69,7 +70,7 @@ async-trait = ["dep:async-trait"]
|
||||||
serialize = ["dep:serde", "uuid/serde"]
|
serialize = ["dep:serde", "uuid/serde"]
|
||||||
# ffi
|
# ffi
|
||||||
rust = [] # used for ci matrix
|
rust = [] # used for ci matrix
|
||||||
java = ["lazy_static", "jni", "tracing-subscriber"]
|
java = ["lazy_static", "jni", "tracing-subscriber", "jni-toolbox"]
|
||||||
js = ["napi-build", "tracing-subscriber", "napi", "napi-derive"]
|
js = ["napi-build", "tracing-subscriber", "napi", "napi-derive"]
|
||||||
py-noabi = ["pyo3", "tracing-subscriber", "pyo3-build-config"]
|
py-noabi = ["pyo3", "tracing-subscriber", "pyo3-build-config"]
|
||||||
py = ["py-noabi", "pyo3/abi3-py38"]
|
py = ["py-noabi", "pyo3/abi3-py38"]
|
||||||
|
|
|
@ -1,95 +1,44 @@
|
||||||
use jni::{objects::{JClass, JObject}, sys::{jboolean, jlong, jobject, jstring}, JNIEnv};
|
use jni::{objects::JObject, JNIEnv};
|
||||||
|
use jni_toolbox::jni;
|
||||||
|
|
||||||
use crate::api::{Controller, TextChange};
|
use crate::{api::{Controller, TextChange}, errors::ControllerError};
|
||||||
|
|
||||||
use super::{handle_error, null_check, tokio, Deobjectify, JExceptable, JObjectify};
|
use super::null_check;
|
||||||
|
|
||||||
/// Gets the name of the buffer.
|
/// Get the name of the buffer.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "BufferController")]
|
||||||
pub extern "system" fn Java_mp_code_BufferController_get_1name(
|
fn get_name(controller: &mut crate::buffer::Controller) -> String {
|
||||||
mut env: JNIEnv,
|
controller.path().to_string() //TODO: &str is built into the newer version
|
||||||
_class: JClass,
|
|
||||||
self_ptr: jlong,
|
|
||||||
) -> jstring {
|
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) };
|
|
||||||
let content = controller.path();
|
|
||||||
env.new_string(content)
|
|
||||||
.jexcept(&mut env)
|
|
||||||
.as_raw()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the contents of the buffers.
|
/// Get the contents of the buffers.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "BufferController")]
|
||||||
pub extern "system" fn Java_mp_code_BufferController_get_1content(
|
fn get_content(controller: &mut crate::buffer::Controller) -> Result<String, ControllerError> {
|
||||||
mut env: JNIEnv,
|
super::tokio().block_on(controller.content())
|
||||||
_class: JClass,
|
|
||||||
self_ptr: jlong,
|
|
||||||
) -> jstring {
|
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) };
|
|
||||||
let content = tokio().block_on(controller.content())
|
|
||||||
.jexcept(&mut env);
|
|
||||||
env.new_string(content)
|
|
||||||
.jexcept(&mut env)
|
|
||||||
.as_raw()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to fetch a [TextChange], or returns null if there's nothing.
|
/// Try to fetch a [TextChange], or return null if there's nothing.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "BufferController")]
|
||||||
pub extern "system" fn Java_mp_code_BufferController_try_1recv(
|
fn try_recv(controller: &mut crate::buffer::Controller) -> Result<Option<TextChange>, ControllerError> {
|
||||||
mut env: JNIEnv,
|
super::tokio().block_on(controller.try_recv())
|
||||||
_class: JClass,
|
|
||||||
self_ptr: jlong,
|
|
||||||
) -> jobject {
|
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) };
|
|
||||||
tokio().block_on(controller.try_recv())
|
|
||||||
.jexcept(&mut env)
|
|
||||||
.map(|change| change.jobjectify(&mut env).jexcept(&mut env).as_raw())
|
|
||||||
.unwrap_or_else(std::ptr::null_mut)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Blocks until it receives a [TextChange].
|
/// Block until it receives a [TextChange].
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "BufferController")]
|
||||||
pub extern "system" fn Java_mp_code_BufferController_recv(
|
fn recv(controller: &mut crate::buffer::Controller) -> Result<TextChange, ControllerError> {
|
||||||
mut env: JNIEnv,
|
super::tokio().block_on(controller.recv())
|
||||||
_class: JClass,
|
|
||||||
self_ptr: jlong,
|
|
||||||
) -> jobject {
|
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) };
|
|
||||||
tokio().block_on(controller.recv())
|
|
||||||
.jexcept(&mut env)
|
|
||||||
.jobjectify(&mut env)
|
|
||||||
.jexcept(&mut env)
|
|
||||||
.as_raw()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Receive from Java, converts and sends a [TextChange].
|
/// Send a [TextChange] to the server.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "BufferController")]
|
||||||
pub extern "system" fn Java_mp_code_BufferController_send<'local>(
|
fn send(controller: &mut crate::buffer::Controller, change: TextChange) -> Result<(), ControllerError> {
|
||||||
mut env: JNIEnv<'local>,
|
super::tokio().block_on(controller.send(change))
|
||||||
_class: JClass<'local>,
|
|
||||||
self_ptr: jlong,
|
|
||||||
change: JObject<'local>,
|
|
||||||
) {
|
|
||||||
null_check!(env, change, {});
|
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) };
|
|
||||||
let change = TextChange::deobjectify(&mut env, change);
|
|
||||||
if let Ok(change) = change {
|
|
||||||
tokio().block_on(controller.send(change)).jexcept(&mut env)
|
|
||||||
} else {
|
|
||||||
handle_error!(&mut env, change, {});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Registers a callback for buffer changes.
|
/// Register a callback for buffer changes.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "BufferController")]
|
||||||
pub extern "system" fn Java_mp_code_BufferController_callback<'local>(
|
fn callback<'local>(env: &mut JNIEnv<'local>, controller: &mut crate::buffer::Controller, cb: JObject<'local>) {
|
||||||
mut env: JNIEnv,
|
|
||||||
_class: JClass<'local>,
|
|
||||||
self_ptr: jlong,
|
|
||||||
cb: JObject<'local>,
|
|
||||||
) {
|
|
||||||
null_check!(env, cb, {});
|
null_check!(env, cb, {});
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) };
|
|
||||||
let Ok(cb_ref) = env.new_global_ref(cb) else {
|
let Ok(cb_ref) = env.new_global_ref(cb) else {
|
||||||
env.throw_new("mp/code/exceptions/JNIException", "Failed to pin callback reference!")
|
env.throw_new("mp/code/exceptions/JNIException", "Failed to pin callback reference!")
|
||||||
.expect("Failed to throw exception!");
|
.expect("Failed to throw exception!");
|
||||||
|
@ -101,8 +50,8 @@ pub extern "system" fn Java_mp_code_BufferController_callback<'local>(
|
||||||
let mut env = jvm.attach_current_thread_permanently()
|
let mut env = jvm.attach_current_thread_permanently()
|
||||||
.expect("failed attaching to main JVM thread");
|
.expect("failed attaching to main JVM thread");
|
||||||
if let Err(e) = env.with_local_frame(5, |env| {
|
if let Err(e) = env.with_local_frame(5, |env| {
|
||||||
use crate::ffi::java::JObjectify;
|
use jni_toolbox::IntoJavaObject;
|
||||||
let jcontroller = controller.jobjectify(env)?;
|
let jcontroller = controller.into_java_object(env)?;
|
||||||
if let Err(e) = env.call_method(
|
if let Err(e) = env.call_method(
|
||||||
&cb_ref,
|
&cb_ref,
|
||||||
"accept",
|
"accept",
|
||||||
|
@ -119,46 +68,26 @@ pub extern "system" fn Java_mp_code_BufferController_callback<'local>(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clears the callback for buffer changes.
|
/// Clear the callback for buffer changes.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "BufferController")]
|
||||||
pub extern "system" fn Java_mp_code_BufferController_clear_1callback(
|
fn clear_callback(controller: &mut crate::buffer::Controller) {
|
||||||
_env: JNIEnv,
|
controller.clear_callback()
|
||||||
_class: JClass,
|
|
||||||
self_ptr: jlong,
|
|
||||||
) {
|
|
||||||
unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) }
|
|
||||||
.clear_callback();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Blocks until there is a new value available.
|
/// Block until there is a new value available.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "BufferController")]
|
||||||
pub extern "system" fn Java_mp_code_BufferController_poll(
|
fn poll(controller: &mut crate::buffer::Controller) -> Result<(), ControllerError> {
|
||||||
mut env: JNIEnv,
|
super::tokio().block_on(controller.poll())
|
||||||
_class: JClass,
|
|
||||||
self_ptr: jlong,
|
|
||||||
) {
|
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) };
|
|
||||||
tokio().block_on(controller.poll())
|
|
||||||
.jexcept(&mut env);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stops the controller.
|
/// Stop the controller.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "BufferController")]
|
||||||
pub extern "system" fn Java_mp_code_BufferController_stop(
|
fn stop(controller: &mut crate::buffer::Controller) -> bool {
|
||||||
_env: JNIEnv,
|
controller.stop()
|
||||||
_class: JClass,
|
|
||||||
self_ptr: jlong,
|
|
||||||
) -> jboolean {
|
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) };
|
|
||||||
controller.stop() as jboolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called by the Java GC to drop a [crate::buffer::Controller].
|
/// Called by the Java GC to drop a [crate::buffer::Controller].
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "BufferController")]
|
||||||
pub extern "system" fn Java_mp_code_BufferController_free(
|
fn free(input: jni::sys::jlong) {
|
||||||
_env: JNIEnv,
|
let _ = unsafe { Box::from_raw(input as *mut crate::buffer::Controller) };
|
||||||
_class: JClass,
|
|
||||||
self_ptr: jlong,
|
|
||||||
) {
|
|
||||||
let _ = unsafe { Box::from_raw(self_ptr as *mut crate::buffer::Controller) };
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,231 +1,74 @@
|
||||||
use jni::{objects::{JClass, JObject, JString}, sys::{jboolean, jlong, jobject, jobjectArray}, JNIEnv};
|
use jni_toolbox::jni;
|
||||||
use crate::{api::Config, client::Client, ffi::java::{handle_error, null_check}, Workspace};
|
use crate::{api::Config, client::Client, errors::{ConnectionError, RemoteError}, Workspace};
|
||||||
|
|
||||||
use super::{Deobjectify, JExceptable, JObjectify, tokio};
|
|
||||||
|
|
||||||
/// Connect using the given credentials to the default server, and return a [Client] to interact with it.
|
/// Connect using the given credentials to the default server, and return a [Client] to interact with it.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Client", ptr)]
|
||||||
pub extern "system" fn Java_mp_code_Client_connect<'local>(
|
fn connect(config: Config) -> Result<Client, ConnectionError> {
|
||||||
mut env: JNIEnv<'local>,
|
super::tokio().block_on(Client::connect(config))
|
||||||
_class: JClass<'local>,
|
|
||||||
config: JObject<'local>
|
|
||||||
) -> jobject {
|
|
||||||
null_check!(env, config, std::ptr::null_mut());
|
|
||||||
let config = Config::deobjectify(&mut env, config);
|
|
||||||
if config.is_err() {
|
|
||||||
handle_error!(&mut env, config, std::ptr::null_mut());
|
|
||||||
}
|
|
||||||
|
|
||||||
let client = tokio().block_on(Client::connect(config.unwrap()));
|
|
||||||
if let Ok(client) = client {
|
|
||||||
client.jobjectify(&mut env).jexcept(&mut env).as_raw()
|
|
||||||
} else {
|
|
||||||
handle_error!(&mut env, client, std::ptr::null_mut());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the current [crate::api::User].
|
/// Gets the current [crate::api::User].
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Client", ptr)]
|
||||||
pub extern "system" fn Java_mp_code_Client_get_1user(
|
fn get_user(client: &mut Client) -> crate::api::User {
|
||||||
mut env: JNIEnv,
|
|
||||||
_class: JClass,
|
|
||||||
self_ptr: jlong
|
|
||||||
) -> jobject {
|
|
||||||
let client = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Client)) };
|
|
||||||
client.user().clone()
|
client.user().clone()
|
||||||
.jobjectify(&mut env)
|
|
||||||
.jexcept(&mut env)
|
|
||||||
.as_raw()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Join a [Workspace] and return a pointer to it.
|
/// Join a [Workspace] and return a pointer to it.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Client")]
|
||||||
pub extern "system" fn Java_mp_code_Client_join_1workspace<'local>(
|
fn join_workspace(client: &mut Client, workspace: String) -> Result<Workspace, ConnectionError> {
|
||||||
mut env: JNIEnv<'local>,
|
super::tokio().block_on(client.join_workspace(workspace))
|
||||||
_class: JClass<'local>,
|
|
||||||
self_ptr: jlong,
|
|
||||||
workspace_id: JString<'local>
|
|
||||||
) -> jobject {
|
|
||||||
null_check!(env, workspace_id, std::ptr::null_mut());
|
|
||||||
let client = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Client)) };
|
|
||||||
let workspace_id = unsafe { env.get_string_unchecked(&workspace_id) }
|
|
||||||
.map(|wid| wid.to_string_lossy().to_string())
|
|
||||||
.jexcept(&mut env);
|
|
||||||
let workspace = tokio().block_on(client.join_workspace(workspace_id))
|
|
||||||
.map(|workspace| spawn_updater(workspace.clone()));
|
|
||||||
if let Ok(workspace) = workspace {
|
|
||||||
workspace.jobjectify(&mut env).jexcept(&mut env).as_raw()
|
|
||||||
} else {
|
|
||||||
handle_error!(&mut env, workspace, std::ptr::null_mut())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a workspace on server, if allowed to.
|
/// Create a workspace on server, if allowed to.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Client")]
|
||||||
pub extern "system" fn Java_mp_code_Client_create_1workspace<'local>(
|
fn create_workspace(client: &mut Client, workspace: String) -> Result<(), RemoteError> {
|
||||||
mut env: JNIEnv<'local>,
|
super::tokio().block_on(client.create_workspace(workspace))
|
||||||
_class: JClass<'local>,
|
|
||||||
self_ptr: jlong,
|
|
||||||
workspace_id: JString<'local>
|
|
||||||
) {
|
|
||||||
null_check!(env, workspace_id, {});
|
|
||||||
let client = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Client)) };
|
|
||||||
let workspace_id = unsafe { env.get_string_unchecked(&workspace_id) }
|
|
||||||
.map(|wid| wid.to_string_lossy().to_string())
|
|
||||||
.jexcept(&mut env);
|
|
||||||
tokio()
|
|
||||||
.block_on(client.create_workspace(workspace_id))
|
|
||||||
.jexcept(&mut env);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete a workspace on server, if allowed to.
|
/// Delete a workspace on server, if allowed to.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Client")]
|
||||||
pub extern "system" fn Java_mp_code_Client_delete_1workspace<'local>(
|
fn delete_workspace(client: &mut Client, workspace: String) -> Result<(), RemoteError> {
|
||||||
mut env: JNIEnv<'local>,
|
super::tokio().block_on(client.delete_workspace(workspace))
|
||||||
_class: JClass<'local>,
|
|
||||||
self_ptr: jlong,
|
|
||||||
workspace_id: JString<'local>
|
|
||||||
) {
|
|
||||||
null_check!(env, workspace_id, {});
|
|
||||||
let client = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Client)) };
|
|
||||||
let workspace_id = unsafe { env.get_string_unchecked(&workspace_id) }
|
|
||||||
.map(|wid| wid.to_string_lossy().to_string())
|
|
||||||
.jexcept(&mut env);
|
|
||||||
tokio()
|
|
||||||
.block_on(client.delete_workspace(workspace_id))
|
|
||||||
.jexcept(&mut env);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Invite another user to an owned workspace.
|
/// Invite another user to an owned workspace.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Client")]
|
||||||
pub extern "system" fn Java_mp_code_Client_invite_1to_1workspace<'local>(
|
fn invite_to_workspace(client: &mut Client, workspace: String, user: String) -> Result<(), RemoteError> {
|
||||||
mut env: JNIEnv<'local>,
|
super::tokio().block_on(client.invite_to_workspace(workspace, user))
|
||||||
_class: JClass<'local>,
|
|
||||||
self_ptr: jlong,
|
|
||||||
workspace_id: JString<'local>,
|
|
||||||
user: JString<'local>
|
|
||||||
) {
|
|
||||||
null_check!(env, workspace_id, {});
|
|
||||||
null_check!(env, user, {});
|
|
||||||
let client = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Client)) };
|
|
||||||
let workspace_id = unsafe { env.get_string_unchecked(&workspace_id) }
|
|
||||||
.map(|wid| wid.to_string_lossy().to_string())
|
|
||||||
.jexcept(&mut env);
|
|
||||||
let user_name = unsafe { env.get_string_unchecked(&user) }
|
|
||||||
.map(|wid| wid.to_string_lossy().to_string())
|
|
||||||
.jexcept(&mut env);
|
|
||||||
tokio()
|
|
||||||
.block_on(client.invite_to_workspace(workspace_id, user_name))
|
|
||||||
.jexcept(&mut env);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List available workspaces.
|
/// List available workspaces.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Client")]
|
||||||
pub extern "system" fn Java_mp_code_Client_list_1workspaces<'local>(
|
fn list_workspaces(client: &mut Client, owned: bool, invited: bool) -> Result<Vec<String>, RemoteError> {
|
||||||
mut env: JNIEnv<'local>,
|
super::tokio().block_on(client.list_workspaces(owned, invited))
|
||||||
_class: JClass<'local>,
|
|
||||||
self_ptr: jlong,
|
|
||||||
owned: jboolean,
|
|
||||||
invited: jboolean
|
|
||||||
) -> jobjectArray {
|
|
||||||
let client = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Client)) };
|
|
||||||
let list = tokio()
|
|
||||||
.block_on(client.list_workspaces(owned != 0, invited != 0))
|
|
||||||
.jexcept(&mut env);
|
|
||||||
env.find_class("java/lang/String")
|
|
||||||
.and_then(|class| env.new_object_array(list.len() as i32, class, JObject::null()))
|
|
||||||
.inspect(|arr| {
|
|
||||||
for (idx, path) in list.iter().enumerate() {
|
|
||||||
env.new_string(path)
|
|
||||||
.and_then(|path| env.set_object_array_element(arr, idx as i32, path))
|
|
||||||
.jexcept(&mut env)
|
|
||||||
}
|
|
||||||
}).jexcept(&mut env).as_raw()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List available workspaces.
|
/// List available workspaces.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Client")]
|
||||||
pub extern "system" fn Java_mp_code_Client_active_1workspaces<'local>(
|
fn active_workspaces(client: &mut Client) -> Vec<String> {
|
||||||
mut env: JNIEnv<'local>,
|
client.active_workspaces()
|
||||||
_class: JClass<'local>,
|
|
||||||
self_ptr: jlong
|
|
||||||
) -> jobjectArray {
|
|
||||||
let client = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Client)) };
|
|
||||||
let list = client.active_workspaces();
|
|
||||||
env.find_class("java/lang/String")
|
|
||||||
.and_then(|class| env.new_object_array(list.len() as i32, class, JObject::null()))
|
|
||||||
.inspect(|arr| {
|
|
||||||
for (idx, path) in list.iter().enumerate() {
|
|
||||||
env.new_string(path)
|
|
||||||
.and_then(|path| env.set_object_array_element(arr, idx as i32, path))
|
|
||||||
.jexcept(&mut env)
|
|
||||||
}
|
|
||||||
}).jexcept(&mut env).as_raw()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: this stays until we get rid of the arc then i'll have to find a better way
|
|
||||||
fn spawn_updater(workspace: Workspace) -> Workspace {
|
|
||||||
let w = workspace.clone();
|
|
||||||
tokio().spawn(async move {
|
|
||||||
loop {
|
|
||||||
tokio::time::sleep(std::time::Duration::from_secs(60)).await;
|
|
||||||
w.fetch_buffers().await.unwrap();
|
|
||||||
w.fetch_users().await.unwrap();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
workspace
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Leave a [Workspace] and return whether or not the client was in such workspace.
|
/// Leave a [Workspace] and return whether or not the client was in such workspace.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Client")]
|
||||||
pub extern "system" fn Java_mp_code_Client_leave_1workspace<'local>(
|
fn leave_workspace(client: &mut Client, workspace: String) -> bool {
|
||||||
mut env: JNIEnv<'local>,
|
client.leave_workspace(&workspace)
|
||||||
_class: JClass<'local>,
|
|
||||||
self_ptr: jlong,
|
|
||||||
workspace_id: JString<'local>
|
|
||||||
) -> jboolean {
|
|
||||||
null_check!(env, workspace_id, false as jboolean);
|
|
||||||
let client = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Client)) };
|
|
||||||
unsafe { env.get_string_unchecked(&workspace_id) }
|
|
||||||
.map(|wid| wid.to_string_lossy().to_string())
|
|
||||||
.map(|wid| client.leave_workspace(&wid) as jboolean)
|
|
||||||
.jexcept(&mut env)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a [Workspace] by name and returns a pointer to it.
|
/// Get a [Workspace] by name and returns a pointer to it.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Client")]
|
||||||
pub extern "system" fn Java_mp_code_Client_get_1workspace<'local>(
|
fn get_workspace(client: &mut Client, workspace: String) -> Option<Workspace> {
|
||||||
mut env: JNIEnv<'local>,
|
client.get_workspace(&workspace)
|
||||||
_class: JClass<'local>,
|
|
||||||
self_ptr: jlong,
|
|
||||||
workspace_id: JString<'local>
|
|
||||||
) -> jobject {
|
|
||||||
null_check!(env, workspace_id, std::ptr::null_mut());
|
|
||||||
let client = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Client)) };
|
|
||||||
let workspace_id = unsafe { env.get_string_unchecked(&workspace_id) }
|
|
||||||
.map(|wid| wid.to_string_lossy().to_string())
|
|
||||||
.jexcept(&mut env);
|
|
||||||
if let Some(workspace) = client.get_workspace(&workspace_id) {
|
|
||||||
workspace.jobjectify(&mut env).jexcept(&mut env).as_raw()
|
|
||||||
} else {
|
|
||||||
std::ptr::null_mut()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Refresh the client's session token.
|
/// Refresh the client's session token.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Client")]
|
||||||
pub extern "system" fn Java_mp_code_Client_refresh<'local>(
|
fn refresh(client: &mut Client) -> Result<(), RemoteError> {
|
||||||
mut env: JNIEnv<'local>,
|
super::tokio().block_on(client.refresh())
|
||||||
_class: JClass<'local>,
|
|
||||||
self_ptr: jlong,
|
|
||||||
) {
|
|
||||||
let client = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Client)) };
|
|
||||||
tokio().block_on(client.refresh())
|
|
||||||
.jexcept(&mut env);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called by the Java GC to drop a [Client].
|
/// Called by the Java GC to drop a [Client].
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Client")]
|
||||||
pub extern "system" fn Java_mp_code_Client_free(_env: JNIEnv, _class: JClass, input: jlong) {
|
fn free(input: jni::sys::jlong) {
|
||||||
let _ = unsafe { Box::from_raw(input as *mut Client) };
|
let _ = unsafe { Box::from_raw(input as *mut Client) };
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,66 +1,31 @@
|
||||||
use jni::{objects::{JClass, JObject}, sys::{jboolean, jlong, jobject}, JNIEnv};
|
use jni::{objects::JObject, JNIEnv};
|
||||||
use crate::api::{Controller, Cursor};
|
use jni_toolbox::jni;
|
||||||
|
use crate::{api::{Controller, Cursor}, errors::ControllerError};
|
||||||
|
|
||||||
use super::{handle_error, null_check, tokio, Deobjectify, JExceptable, JObjectify};
|
use super::null_check;
|
||||||
|
|
||||||
/// Try to fetch a [Cursor], or returns null if there's nothing.
|
/// Try to fetch a [Cursor], or returns null if there's nothing.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "CursorController")]
|
||||||
pub extern "system" fn Java_mp_code_CursorController_try_1recv(
|
fn try_recv(controller: &mut crate::cursor::Controller) -> Result<Option<Cursor>, ControllerError> {
|
||||||
mut env: JNIEnv,
|
super::tokio().block_on(controller.try_recv())
|
||||||
_class: JClass,
|
|
||||||
self_ptr: jlong,
|
|
||||||
) -> jobject {
|
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::cursor::Controller)) };
|
|
||||||
tokio().block_on(controller.try_recv())
|
|
||||||
.jexcept(&mut env)
|
|
||||||
.map(|change| change.jobjectify(&mut env).jexcept(&mut env).as_raw())
|
|
||||||
.unwrap_or_else(std::ptr::null_mut)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Block until it receives a [Cursor].
|
/// Block until it receives a [Cursor].
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "CursorController")]
|
||||||
pub extern "system" fn Java_mp_code_CursorController_recv(
|
fn recv(controller: &mut crate::cursor::Controller) -> Result<Cursor, ControllerError> {
|
||||||
mut env: JNIEnv,
|
super::tokio().block_on(controller.recv())
|
||||||
_class: JClass,
|
|
||||||
self_ptr: jlong,
|
|
||||||
) -> jobject {
|
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::cursor::Controller)) };
|
|
||||||
tokio().block_on(controller.recv())
|
|
||||||
.jexcept(&mut env)
|
|
||||||
.jobjectify(&mut env)
|
|
||||||
.jexcept(&mut env)
|
|
||||||
.as_raw()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Receive from Java, converts and sends a [Cursor].
|
/// Receive from Java, converts and sends a [Cursor].
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "CursorController")]
|
||||||
pub extern "system" fn Java_mp_code_CursorController_send<'local>(
|
fn send(controller: &mut crate::cursor::Controller, cursor: Cursor) -> Result<(), ControllerError> {
|
||||||
mut env: JNIEnv<'local>,
|
super::tokio().block_on(controller.send(cursor))
|
||||||
_class: JClass<'local>,
|
|
||||||
self_ptr: jlong,
|
|
||||||
cursor: JObject<'local>,
|
|
||||||
) {
|
|
||||||
null_check!(env, cursor, {});
|
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::cursor::Controller)) };
|
|
||||||
let cursor = Cursor::deobjectify(&mut env, cursor);
|
|
||||||
if let Ok(cursor) = cursor {
|
|
||||||
tokio().block_on(controller.send(cursor)).jexcept(&mut env)
|
|
||||||
} else {
|
|
||||||
handle_error!(&mut env, cursor, {});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Registers a callback for cursor changes.
|
/// Register a callback for cursor changes.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "CursorController")]
|
||||||
pub extern "system" fn Java_mp_code_CursorController_callback<'local>(
|
fn callback<'local>(env: &mut JNIEnv<'local>, controller: &mut crate::cursor::Controller, cb: JObject<'local>) {
|
||||||
mut env: JNIEnv,
|
null_check!(env, cb, {});
|
||||||
_class: JClass<'local>,
|
|
||||||
self_ptr: jlong,
|
|
||||||
cb: JObject<'local>,
|
|
||||||
) {
|
|
||||||
null_check!(env, cb, {});
|
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::cursor::Controller)) };
|
|
||||||
|
|
||||||
let Ok(cb_ref) = env.new_global_ref(cb) else {
|
let Ok(cb_ref) = env.new_global_ref(cb) else {
|
||||||
env.throw_new("mp/code/exceptions/JNIException", "Failed to pin callback reference!")
|
env.throw_new("mp/code/exceptions/JNIException", "Failed to pin callback reference!")
|
||||||
.expect("Failed to throw exception!");
|
.expect("Failed to throw exception!");
|
||||||
|
@ -72,8 +37,8 @@ pub extern "system" fn Java_mp_code_CursorController_callback<'local>(
|
||||||
let mut env = jvm.attach_current_thread_permanently()
|
let mut env = jvm.attach_current_thread_permanently()
|
||||||
.expect("failed attaching to main JVM thread");
|
.expect("failed attaching to main JVM thread");
|
||||||
if let Err(e) = env.with_local_frame(5, |env| {
|
if let Err(e) = env.with_local_frame(5, |env| {
|
||||||
use crate::ffi::java::JObjectify;
|
use jni_toolbox::IntoJavaObject;
|
||||||
let jcontroller = controller.jobjectify(env)?;
|
let jcontroller = controller.into_java_object(env)?;
|
||||||
if let Err(e) = env.call_method(
|
if let Err(e) = env.call_method(
|
||||||
&cb_ref,
|
&cb_ref,
|
||||||
"accept",
|
"accept",
|
||||||
|
@ -90,46 +55,26 @@ pub extern "system" fn Java_mp_code_CursorController_callback<'local>(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clears the callback for cursor changes.
|
/// Clear the callback for cursor changes.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "CursorController")]
|
||||||
pub extern "system" fn Java_mp_code_CursorController_clear_1callback(
|
fn clear_callback(controller: &mut crate::cursor::Controller) {
|
||||||
_env: JNIEnv,
|
controller.clear_callback()
|
||||||
_class: JClass,
|
|
||||||
self_ptr: jlong,
|
|
||||||
) {
|
|
||||||
unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::cursor::Controller)) }
|
|
||||||
.clear_callback();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Blocks until there is a new value available.
|
/// Block until there is a new value available.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "CursorController")]
|
||||||
pub extern "system" fn Java_mp_code_CursorController_poll(
|
fn poll(controller: &mut crate::cursor::Controller) -> Result<(), ControllerError> {
|
||||||
mut env: JNIEnv,
|
super::tokio().block_on(controller.poll())
|
||||||
_class: JClass,
|
|
||||||
self_ptr: jlong,
|
|
||||||
) {
|
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::cursor::Controller)) };
|
|
||||||
tokio().block_on(controller.poll())
|
|
||||||
.jexcept(&mut env);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stops the controller.
|
/// Stop the controller.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "CursorController")]
|
||||||
pub extern "system" fn Java_mp_code_CursorController_stop(
|
fn stop(controller: &mut crate::cursor::Controller) -> bool {
|
||||||
_env: JNIEnv,
|
controller.stop()
|
||||||
_class: JClass,
|
|
||||||
self_ptr: jlong,
|
|
||||||
) -> jboolean {
|
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::cursor::Controller)) };
|
|
||||||
controller.stop() as jboolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called by the Java GC to drop a [crate::cursor::Controller].
|
/// Called by the Java GC to drop a [crate::cursor::Controller].
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "CursorController")]
|
||||||
pub extern "system" fn Java_mp_code_CursorController_free(
|
fn free(input: jni::sys::jlong) {
|
||||||
_env: JNIEnv,
|
let _ = unsafe { Box::from_raw(input as *mut crate::cursor::Controller) };
|
||||||
_class: JClass,
|
|
||||||
self_ptr: jlong,
|
|
||||||
) {
|
|
||||||
let _ = unsafe { Box::from_raw(self_ptr as *mut crate::cursor::Controller) };
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,16 @@
|
||||||
use jni::{objects::{JClass, JString}, sys::{jboolean, jlong}, JNIEnv};
|
use jni_toolbox::jni;
|
||||||
|
|
||||||
use super::{JExceptable, null_check};
|
|
||||||
|
|
||||||
/// Calculate the XXH3 hash for a given String.
|
/// Calculate the XXH3 hash for a given String.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Extensions")]
|
||||||
pub extern "system" fn Java_mp_code_Extensions_hash<'local>(
|
fn hash(content: String) -> i64 {
|
||||||
mut env: JNIEnv,
|
|
||||||
_class: JClass<'local>,
|
|
||||||
content: JString<'local>,
|
|
||||||
) -> jlong {
|
|
||||||
null_check!(env, content, 0 as jlong);
|
|
||||||
let content: String = env.get_string(&content)
|
|
||||||
.map(|s| s.into())
|
|
||||||
.jexcept(&mut env);
|
|
||||||
let hash = crate::ext::hash(content.as_bytes());
|
let hash = crate::ext::hash(content.as_bytes());
|
||||||
i64::from_ne_bytes(hash.to_ne_bytes())
|
i64::from_ne_bytes(hash.to_ne_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tells the [tokio] runtime how to drive the event loop.
|
/// Tells the [tokio] runtime how to drive the event loop.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Extensions")]
|
||||||
pub extern "system" fn Java_mp_code_Extensions_drive(
|
fn drive(block: bool) {
|
||||||
_env: JNIEnv,
|
if block {
|
||||||
_class: JClass,
|
|
||||||
block: jboolean
|
|
||||||
) {
|
|
||||||
if block != 0 {
|
|
||||||
super::tokio().block_on(std::future::pending::<()>());
|
super::tokio().block_on(std::future::pending::<()>());
|
||||||
} else {
|
} else {
|
||||||
std::thread::spawn(|| {
|
std::thread::spawn(|| {
|
||||||
|
@ -34,19 +20,8 @@ pub extern "system" fn Java_mp_code_Extensions_drive(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set up the tracing subscriber.
|
/// Set up the tracing subscriber.
|
||||||
#[no_mangle]
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub extern "system" fn Java_mp_code_Extensions_setupTracing<'local>(
|
#[jni(package = "mp.code", class = "Extensions")]
|
||||||
mut env: JNIEnv,
|
fn setupTracing(path: Option<String>, debug: bool) {
|
||||||
_class: JClass<'local>,
|
super::setup_logger(debug, path);
|
||||||
path: JString<'local>,
|
|
||||||
debug: jboolean
|
|
||||||
) {
|
|
||||||
super::setup_logger(
|
|
||||||
debug != 0,
|
|
||||||
Some(path)
|
|
||||||
.filter(|p| !p.is_null())
|
|
||||||
.map(|p| env.get_string(&p).map(|s| s.into())
|
|
||||||
.jexcept(&mut env))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,26 +62,9 @@ pub(crate) fn setup_logger(debug: bool, path: Option<String>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Utility macro that attempts to handle an error in a [Result].
|
|
||||||
/// MUST be called within a $result.is_err() block or similar. Failure to do so is UB.
|
|
||||||
/// Will return early with a provided return value, or panic if it fails to throw a Java exception.
|
|
||||||
macro_rules! handle_error {
|
|
||||||
($env: expr, $result: ident, $return: expr) => {
|
|
||||||
{
|
|
||||||
let err = unsafe { $result.unwrap_err_unchecked() };
|
|
||||||
tracing::info!("Attempting to throw error {err:#?} as a Java exception...");
|
|
||||||
if let Err(e) = err.jobjectify($env).map(|t| t.into()).and_then(|t: jni::objects::JThrowable| $env.throw(&t)) {
|
|
||||||
panic!("Failed to throw exception: {e}");
|
|
||||||
}
|
|
||||||
return $return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
pub(crate) use handle_error;
|
|
||||||
|
|
||||||
/// Performs a null check on the given variable and throws a NullPointerException on the Java side
|
/// Performs a null check on the given variable and throws a NullPointerException on the Java side
|
||||||
/// if it is null. Finally, it returns with the given default value.
|
/// if it is null. Finally, it returns with the given default value.
|
||||||
macro_rules! null_check {
|
macro_rules! null_check { // TODO replace
|
||||||
($env: ident, $var: ident, $return: expr) => {
|
($env: ident, $var: ident, $return: expr) => {
|
||||||
if $var.is_null() {
|
if $var.is_null() {
|
||||||
let mut message = stringify!($var).to_string();
|
let mut message = stringify!($var).to_string();
|
||||||
|
@ -94,76 +77,37 @@ macro_rules! null_check {
|
||||||
}
|
}
|
||||||
pub(crate) use null_check;
|
pub(crate) use null_check;
|
||||||
|
|
||||||
|
impl jni_toolbox::JniToolboxError for crate::errors::ConnectionError {
|
||||||
/// A trait meant for our local result type to make converting it to Java easier.
|
fn jclass(&self) -> String {
|
||||||
/// jni-rs technically has [jni::errors::ToException], but this approach keeps it stream-like.
|
match self {
|
||||||
pub(crate) trait JExceptable<'local, T: Default> {
|
crate::errors::ConnectionError::Transport(_) => "mp/code/exceptions/ConnectionTransportException",
|
||||||
/// Unwrap it and throws an appropriate Java exception if it's an error.
|
crate::errors::ConnectionError::Remote(_) => "mp/code/exceptions/ConnectionRemoteException"
|
||||||
/// Theoretically it returns the type's default value, but the exception makes the value ignored.
|
}.to_string()
|
||||||
fn jexcept(self, env: &mut jni::JNIEnv<'local>) -> T;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'local, T: Default, E: JObjectify<'local> + std::fmt::Debug> JExceptable<'local, T> for Result<T, E> {
|
|
||||||
fn jexcept(self, env: &mut jni::JNIEnv<'local>) -> T {
|
|
||||||
if let Ok(res) = self {
|
|
||||||
res
|
|
||||||
} else {
|
|
||||||
handle_error!(env, self, Default::default());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allows easy conversion for various types into Java objects.
|
impl jni_toolbox::JniToolboxError for crate::errors::RemoteError {
|
||||||
/// This is similar to [TryInto], but for Java types.
|
fn jclass(&self) -> String {
|
||||||
pub(crate) trait JObjectify<'local> {
|
"mp/code/exceptions/ConnectionRemoteException".to_string()
|
||||||
/// Attempt to convert the given object to a [jni::objects::JObject].
|
}
|
||||||
fn jobjectify(self, env: &mut jni::JNIEnv<'local>) -> Result<jni::objects::JObject<'local>, jni::errors::Error>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! jobjectify_error {
|
impl jni_toolbox::JniToolboxError for crate::errors::ControllerError {
|
||||||
($self: ident, $type: ty, $jclass: expr) => {
|
fn jclass(&self) -> String {
|
||||||
impl<'local> JObjectify<'local> for $type {
|
match self {
|
||||||
fn jobjectify($self, env: &mut jni::JNIEnv<'local>) -> Result<jni::objects::JObject<'local>, jni::errors::Error> {
|
crate::errors::ControllerError::Stopped => "mp/code/exceptions/ControllerStoppedException",
|
||||||
let class = env.find_class($jclass)?;
|
crate::errors::ControllerError::Unfulfilled => "mp/code/exceptions/ControllerUnfulfilledException",
|
||||||
let msg = env.new_string(format!("{:#?}", $self))?;
|
}.to_string()
|
||||||
env.new_object(class, "(Ljava/lang/String;)V", &[jni::objects::JValueGen::Object(&msg)])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
jobjectify_error!(self, crate::errors::RemoteError, "mp/code/exceptions/ConnectionRemoteException");
|
|
||||||
jobjectify_error!(self, jni::errors::Error, match self {
|
|
||||||
jni::errors::Error::NullPtr(_) => "java/lang/NullPointerException",
|
|
||||||
_ => "mp/code/exceptions/JNIException"
|
|
||||||
});
|
|
||||||
jobjectify_error!(self, uuid::Error, "java/lang/IllegalArgumentException");
|
|
||||||
jobjectify_error!(self, crate::errors::ConnectionError, match self {
|
|
||||||
crate::errors::ConnectionError::Transport(_) => "mp/code/exceptions/ConnectionTransportException",
|
|
||||||
crate::errors::ConnectionError::Remote(_) => "mp/code/exceptions/ConnectionRemoteException"
|
|
||||||
});
|
|
||||||
jobjectify_error!(self, crate::errors::ControllerError, match self {
|
|
||||||
crate::errors::ControllerError::Stopped => "mp/code/exceptions/ControllerStoppedException",
|
|
||||||
crate::errors::ControllerError::Unfulfilled => "mp/code/exceptions/ControllerUnfulfilledException",
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
impl<'local> JObjectify<'local> for uuid::Uuid {
|
|
||||||
fn jobjectify(self, env: &mut jni::JNIEnv<'local>) -> Result<jni::objects::JObject<'local>, jni::errors::Error> {
|
|
||||||
let class = env.find_class("java/util/UUID")?;
|
|
||||||
let (msb, lsb) = self.as_u64_pair();
|
|
||||||
let msb = i64::from_ne_bytes(msb.to_ne_bytes());
|
|
||||||
let lsb = i64::from_ne_bytes(lsb.to_ne_bytes());
|
|
||||||
env.new_object(&class, "(JJ)V", &[jni::objects::JValueGen::Long(msb), jni::objects::JValueGen::Long(lsb)])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a [JObjectify] implementation for a class that is just a holder for a pointer.
|
/// Generates a [JObjectify] implementation for a class that is just a holder for a pointer.
|
||||||
macro_rules! jobjectify_ptr_class {
|
macro_rules! into_java_ptr_class {
|
||||||
($type: ty, $jclass: literal) => {
|
($type: ty, $jclass: literal) => {
|
||||||
impl<'local> JObjectify<'local> for $type {
|
impl<'j> jni_toolbox::IntoJavaObject<'j> for $type {
|
||||||
fn jobjectify(self, env: &mut jni::JNIEnv<'local>) -> Result<jni::objects::JObject<'local>, jni::errors::Error> {
|
const CLASS: &'static str = $jclass;
|
||||||
let class = env.find_class($jclass)?;
|
fn into_java_object(self, env: &mut jni::JNIEnv<'j>) -> Result<jni::objects::JObject<'j>, jni::errors::Error> {
|
||||||
|
let class = env.find_class(Self::CLASS)?;
|
||||||
env.new_object(
|
env.new_object(
|
||||||
class,
|
class,
|
||||||
"(J)V",
|
"(J)V",
|
||||||
|
@ -174,16 +118,17 @@ macro_rules! jobjectify_ptr_class {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
jobjectify_ptr_class!(crate::Client, "mp/code/Client");
|
into_java_ptr_class!(crate::Client, "mp/code/Client");
|
||||||
jobjectify_ptr_class!(crate::Workspace, "mp/code/Workspace");
|
into_java_ptr_class!(crate::Workspace, "mp/code/Workspace");
|
||||||
jobjectify_ptr_class!(crate::cursor::Controller, "mp/code/CursorController");
|
into_java_ptr_class!(crate::cursor::Controller, "mp/code/CursorController");
|
||||||
jobjectify_ptr_class!(crate::buffer::Controller, "mp/code/BufferController");
|
into_java_ptr_class!(crate::buffer::Controller, "mp/code/BufferController");
|
||||||
|
|
||||||
impl<'local> JObjectify<'local> for crate::api::User {
|
impl<'j> jni_toolbox::IntoJavaObject<'j> for crate::api::User {
|
||||||
fn jobjectify(self, env: &mut jni::JNIEnv<'local>) -> Result<jni::objects::JObject<'local>, jni::errors::Error> {
|
const CLASS: &'static str = "mp/code/data/User";
|
||||||
let id_field = self.id.jobjectify(env)?;
|
fn into_java_object(self, env: &mut jni::JNIEnv<'j>) -> Result<jni::objects::JObject<'j>, jni::errors::Error> {
|
||||||
|
let id_field = self.id.into_java_object(env)?;
|
||||||
let name_field = env.new_string(self.name)?;
|
let name_field = env.new_string(self.name)?;
|
||||||
let class = env.find_class("mp/code/data/User")?;
|
let class = env.find_class(Self::CLASS)?;
|
||||||
env.new_object(
|
env.new_object(
|
||||||
&class,
|
&class,
|
||||||
"(Ljava/util/UUID;Ljava/lang/String;)V",
|
"(Ljava/util/UUID;Ljava/lang/String;)V",
|
||||||
|
@ -195,8 +140,9 @@ impl<'local> JObjectify<'local> for crate::api::User {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'local> JObjectify<'local> for crate::api::Event {
|
impl<'j> jni_toolbox::IntoJavaObject<'j> for crate::api::Event {
|
||||||
fn jobjectify(self, env: &mut jni::JNIEnv<'local>) -> Result<jni::objects::JObject<'local>, jni::errors::Error> {
|
const CLASS: &'static str = "mp/code/Workspace$Event";
|
||||||
|
fn into_java_object(self, env: &mut jni::JNIEnv<'j>) -> Result<jni::objects::JObject<'j>, jni::errors::Error> {
|
||||||
let (ordinal, arg) = match self {
|
let (ordinal, arg) = match self {
|
||||||
crate::api::Event::UserJoin(arg) => (0, env.new_string(arg)?),
|
crate::api::Event::UserJoin(arg) => (0, env.new_string(arg)?),
|
||||||
crate::api::Event::UserLeave(arg) => (1, env.new_string(arg)?),
|
crate::api::Event::UserLeave(arg) => (1, env.new_string(arg)?),
|
||||||
|
@ -212,7 +158,7 @@ impl<'local> JObjectify<'local> for crate::api::Event {
|
||||||
)?.l()?.into();
|
)?.l()?.into();
|
||||||
let event_type = env.get_object_array_element(variants, ordinal)?;
|
let event_type = env.get_object_array_element(variants, ordinal)?;
|
||||||
|
|
||||||
let event_class = env.find_class("mp/code/Workspace$Event")?;
|
let event_class = env.find_class(Self::CLASS)?;
|
||||||
env.new_object(
|
env.new_object(
|
||||||
event_class,
|
event_class,
|
||||||
"(Lmp/code/Workspace$Event$Type;Ljava/lang/String;)V",
|
"(Lmp/code/Workspace$Event$Type;Ljava/lang/String;)V",
|
||||||
|
@ -224,15 +170,16 @@ impl<'local> JObjectify<'local> for crate::api::Event {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'local> JObjectify<'local> for crate::workspace::DetachResult {
|
impl<'j> jni_toolbox::IntoJavaObject<'j> for crate::workspace::DetachResult {
|
||||||
fn jobjectify(self, env: &mut jni::JNIEnv<'local>) -> Result<jni::objects::JObject<'local>, jni::errors::Error> {
|
const CLASS: &'static str = "mp/code/data/DetachResult";
|
||||||
|
fn into_java_object(self, env: &mut jni::JNIEnv<'j>) -> Result<jni::objects::JObject<'j>, jni::errors::Error> {
|
||||||
let ordinal = match self {
|
let ordinal = match self {
|
||||||
crate::workspace::DetachResult::NotAttached => 0,
|
crate::workspace::DetachResult::NotAttached => 0,
|
||||||
crate::workspace::DetachResult::Detaching => 1,
|
crate::workspace::DetachResult::Detaching => 1,
|
||||||
crate::workspace::DetachResult::AlreadyDetached => 2
|
crate::workspace::DetachResult::AlreadyDetached => 2
|
||||||
};
|
};
|
||||||
|
|
||||||
let class = env.find_class("mp/code/data/DetachResult")?;
|
let class = env.find_class(Self::CLASS)?;
|
||||||
let variants: jni::objects::JObjectArray = env.call_method(
|
let variants: jni::objects::JObjectArray = env.call_method(
|
||||||
class,
|
class,
|
||||||
"getEnumConstants",
|
"getEnumConstants",
|
||||||
|
@ -243,66 +190,77 @@ impl<'local> JObjectify<'local> for crate::workspace::DetachResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'local> JObjectify<'local> for crate::api::TextChange {
|
impl<'j> jni_toolbox::IntoJavaObject<'j> for crate::api::TextChange {
|
||||||
fn jobjectify(self, env: &mut jni::JNIEnv<'local>) -> Result<jni::objects::JObject<'local>, jni::errors::Error> {
|
const CLASS: &'static str = "mp/code/data/TextChange";
|
||||||
|
fn into_java_object(self, env: &mut jni::JNIEnv<'j>) -> Result<jni::objects::JObject<'j>, jni::errors::Error> {
|
||||||
let content = env.new_string(self.content)?;
|
let content = env.new_string(self.content)?;
|
||||||
|
|
||||||
let hash = env.find_class("java/util/OptionalLong").and_then(|class| {
|
let hash_class = env.find_class("java/util/OptionalLong")?;
|
||||||
if let Some(h) = self.hash {
|
let hash = if let Some(h) = self.hash {
|
||||||
env.call_static_method(class, "of", "(J)Ljava/util/OptionalLong;", &[jni::objects::JValueGen::Long(h)])
|
env.call_static_method(hash_class, "of", "(J)Ljava/util/OptionalLong;", &[jni::objects::JValueGen::Long(h)])
|
||||||
} else {
|
} else {
|
||||||
env.call_static_method(class, "empty", "()Ljava/util/OptionalLong;", &[])
|
env.call_static_method(hash_class, "empty", "()Ljava/util/OptionalLong;", &[])
|
||||||
|
}?.l()?;
|
||||||
|
|
||||||
|
let class = env.find_class(Self::CLASS)?;
|
||||||
|
env.new_object(
|
||||||
|
class,
|
||||||
|
"(JJLjava/lang/String;Ljava/util/OptionalLong;)V",
|
||||||
|
&[
|
||||||
|
jni::objects::JValueGen::Long(self.start.into()),
|
||||||
|
jni::objects::JValueGen::Long(self.end.into()),
|
||||||
|
jni::objects::JValueGen::Object(&content),
|
||||||
|
jni::objects::JValueGen::Object(&hash)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'j> jni_toolbox::IntoJavaObject<'j> for crate::api::Cursor {
|
||||||
|
const CLASS: &'static str = "mp/code/data/Cursor";
|
||||||
|
fn into_java_object(self, env: &mut jni::JNIEnv<'j>) -> Result<jni::objects::JObject<'j>, jni::errors::Error> {
|
||||||
|
let class = env.find_class("mp/code/data/Cursor")?;
|
||||||
|
let buffer = env.new_string(&self.buffer)?;
|
||||||
|
let user = if let Some(user) = self.user {
|
||||||
|
env.new_string(user)?.into()
|
||||||
|
} else {
|
||||||
|
jni::objects::JObject::null()
|
||||||
|
};
|
||||||
|
|
||||||
|
env.new_object(
|
||||||
|
class,
|
||||||
|
"(IIIILjava/lang/String;Ljava/lang/String;)V",
|
||||||
|
&[
|
||||||
|
jni::objects::JValueGen::Int(self.start.0),
|
||||||
|
jni::objects::JValueGen::Int(self.start.1),
|
||||||
|
jni::objects::JValueGen::Int(self.end.0),
|
||||||
|
jni::objects::JValueGen::Int(self.end.1),
|
||||||
|
jni::objects::JValueGen::Object(&buffer),
|
||||||
|
jni::objects::JValueGen::Object(&user)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! from_java_ptr {
|
||||||
|
($type: ty) => {
|
||||||
|
impl<'j> jni_toolbox::FromJava<'j> for &mut $type {
|
||||||
|
type From = jni::sys::jobject;
|
||||||
|
fn from_java(_env: &mut jni::JNIEnv<'j>, value: Self::From) -> Result<Self, jni::errors::Error> {
|
||||||
|
Ok(unsafe { Box::leak(Box::from_raw(value as *mut $type)) })
|
||||||
}
|
}
|
||||||
}).and_then(|o| o.l())?;
|
}
|
||||||
env.find_class("mp/code/data/TextChange").and_then(|class| {
|
};
|
||||||
env.new_object(
|
|
||||||
class,
|
|
||||||
"(JJLjava/lang/String;Ljava/util/OptionalLong;)V",
|
|
||||||
&[
|
|
||||||
jni::objects::JValueGen::Long(self.start.into()),
|
|
||||||
jni::objects::JValueGen::Long(self.end.into()),
|
|
||||||
jni::objects::JValueGen::Object(&content),
|
|
||||||
jni::objects::JValueGen::Object(&hash)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'local> JObjectify<'local> for crate::api::Cursor {
|
from_java_ptr!(crate::Client);
|
||||||
fn jobjectify(self, env: &mut jni::JNIEnv<'local>) -> Result<jni::objects::JObject<'local>, jni::errors::Error> {
|
from_java_ptr!(crate::Workspace);
|
||||||
env.find_class("mp/code/data/Cursor").and_then(|class| {
|
from_java_ptr!(crate::cursor::Controller);
|
||||||
let buffer = env.new_string(&self.buffer)?;
|
from_java_ptr!(crate::buffer::Controller);
|
||||||
let user = if let Some(user) = self.user {
|
|
||||||
env.new_string(user)?.into()
|
|
||||||
} else {
|
|
||||||
jni::objects::JObject::null()
|
|
||||||
};
|
|
||||||
|
|
||||||
env.new_object(
|
impl<'j> jni_toolbox::FromJava<'j> for crate::api::Config {
|
||||||
class,
|
type From = jni::objects::JObject<'j>;
|
||||||
"(IIIILjava/lang/String;Ljava/lang/String;)V",
|
fn from_java(env: &mut jni::JNIEnv<'j>, config: Self::From) -> Result<Self, jni::errors::Error> {
|
||||||
&[
|
|
||||||
jni::objects::JValueGen::Int(self.start.0),
|
|
||||||
jni::objects::JValueGen::Int(self.start.1),
|
|
||||||
jni::objects::JValueGen::Int(self.end.0),
|
|
||||||
jni::objects::JValueGen::Int(self.end.1),
|
|
||||||
jni::objects::JValueGen::Object(&buffer),
|
|
||||||
jni::objects::JValueGen::Object(&user)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allows easy conversion of Java types into their Rust counterparts.
|
|
||||||
pub(crate) trait Deobjectify<'local, T: Sized> {
|
|
||||||
/// Attempt to convert the given [jni::objects::JObject] into its Rust counterpart.
|
|
||||||
fn deobjectify(env: &mut jni::JNIEnv<'local>, jobject: jni::objects::JObject<'local>) -> Result<T, jni::errors::Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'local> Deobjectify<'local, Self> for crate::api::Config {
|
|
||||||
fn deobjectify(env: &mut jni::JNIEnv<'local>, config: jni::objects::JObject<'local>) -> Result<Self, jni::errors::Error> {
|
|
||||||
let username = {
|
let username = {
|
||||||
let jfield = env.get_field(&config, "username", "Ljava/lang/String;")?.l()?;
|
let jfield = env.get_field(&config, "username", "Ljava/lang/String;")?.l()?;
|
||||||
if jfield.is_null() {
|
if jfield.is_null() {
|
||||||
|
@ -359,8 +317,9 @@ impl<'local> Deobjectify<'local, Self> for crate::api::Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'local> Deobjectify<'local, Self> for crate::api::Cursor {
|
impl<'j> jni_toolbox::FromJava<'j> for crate::api::Cursor {
|
||||||
fn deobjectify(env: &mut jni::JNIEnv<'local>, cursor: jni::objects::JObject<'local>) -> Result<Self, jni::errors::Error> {
|
type From = jni::objects::JObject<'j>;
|
||||||
|
fn from_java(env: &mut jni::JNIEnv<'j>, cursor: Self::From) -> Result<Self, jni::errors::Error> {
|
||||||
let start_row = env.get_field(&cursor, "startRow", "I")?.i()?;
|
let start_row = env.get_field(&cursor, "startRow", "I")?.i()?;
|
||||||
let start_col = env.get_field(&cursor, "startCol", "I")?.i()?;
|
let start_col = env.get_field(&cursor, "startCol", "I")?.i()?;
|
||||||
let end_row = env.get_field(&cursor, "endRow", "I")?.i()?;
|
let end_row = env.get_field(&cursor, "endRow", "I")?.i()?;
|
||||||
|
@ -387,8 +346,9 @@ impl<'local> Deobjectify<'local, Self> for crate::api::Cursor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'local> Deobjectify<'local, Self> for crate::api::TextChange {
|
impl<'j> jni_toolbox::FromJava<'j> for crate::api::TextChange {
|
||||||
fn deobjectify(env: &mut jni::JNIEnv<'local>, change: jni::objects::JObject<'local>) -> Result<Self, jni::errors::Error> {
|
type From = jni::objects::JObject<'j>;
|
||||||
|
fn from_java(env: &mut jni::JNIEnv<'j>, change: Self::From) -> Result<Self, jni::errors::Error> {
|
||||||
let start = env.get_field(&change, "start", "J")?.j()?.clamp(0, u32::MAX.into()) as u32;
|
let start = env.get_field(&change, "start", "J")?.j()?.clamp(0, u32::MAX.into()) as u32;
|
||||||
let end = env.get_field(&change, "end", "J")?.j()?.clamp(0, u32::MAX.into()) as u32;
|
let end = env.get_field(&change, "end", "J")?.j()?.clamp(0, u32::MAX.into()) as u32;
|
||||||
|
|
||||||
|
|
|
@ -1,244 +1,86 @@
|
||||||
use jni::{objects::{JClass, JObject, JString}, sys::{jboolean, jlong, jobject, jobjectArray, jstring}, JNIEnv};
|
use jni_toolbox::jni;
|
||||||
use crate::Workspace;
|
use crate::{errors::{ConnectionError, ControllerError, RemoteError}, Workspace};
|
||||||
|
|
||||||
use super::{handle_error, null_check, JExceptable, JObjectify};
|
|
||||||
|
|
||||||
/// Get the workspace id.
|
/// Get the workspace id.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Workspace")]
|
||||||
pub extern "system" fn Java_mp_code_Workspace_get_1workspace_1id<'local>(
|
fn get_workspace_id(workspace: &mut Workspace) -> String {
|
||||||
mut env: JNIEnv<'local>,
|
workspace.id()
|
||||||
_class: JClass<'local>,
|
|
||||||
self_ptr: jlong
|
|
||||||
) -> jstring {
|
|
||||||
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
|
||||||
env.new_string(workspace.id()).jexcept(&mut env).as_raw()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a cursor controller by name and returns a pointer to it.
|
/// Get a cursor controller by name and returns a pointer to it.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Workspace")]
|
||||||
pub extern "system" fn Java_mp_code_Workspace_get_1cursor<'local>(
|
fn get_cursor(workspace: &mut Workspace) -> crate::cursor::Controller {
|
||||||
mut env: JNIEnv<'local>,
|
workspace.cursor()
|
||||||
_class: JClass<'local>,
|
|
||||||
self_ptr: jlong
|
|
||||||
) -> jobject {
|
|
||||||
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
|
||||||
workspace.cursor().jobjectify(&mut env).jexcept(&mut env).as_raw()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a buffer controller by name and returns a pointer to it.
|
/// Get a buffer controller by name and returns a pointer to it.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Workspace")]
|
||||||
pub extern "system" fn Java_mp_code_Workspace_get_1buffer<'local>(
|
fn get_buffer(workspace: &mut Workspace, path: String) -> Option<crate::buffer::Controller> {
|
||||||
mut env: JNIEnv<'local>,
|
|
||||||
_class: JClass<'local>,
|
|
||||||
self_ptr: jlong,
|
|
||||||
path: JString<'local>
|
|
||||||
) -> jobject {
|
|
||||||
null_check!(env, path, std::ptr::null_mut());
|
|
||||||
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
|
||||||
let path = unsafe { env.get_string_unchecked(&path) }
|
|
||||||
.map(|path| path.to_string_lossy().to_string())
|
|
||||||
.jexcept(&mut env);
|
|
||||||
workspace.buffer_by_name(&path)
|
workspace.buffer_by_name(&path)
|
||||||
.map(|buf| buf.jobjectify(&mut env).jexcept(&mut env))
|
|
||||||
.unwrap_or_default()
|
|
||||||
.as_raw()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the filetree.
|
/// Get the filetree.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Workspace")]
|
||||||
pub extern "system" fn Java_mp_code_Workspace_get_1file_1tree(
|
fn get_file_tree(workspace: &mut Workspace, filter: Option<String>, strict: bool) -> Vec<String> {
|
||||||
mut env: JNIEnv,
|
workspace.filetree(filter.as_deref(), strict)
|
||||||
_class: JClass,
|
|
||||||
self_ptr: jlong,
|
|
||||||
filter: JString,
|
|
||||||
strict: jboolean
|
|
||||||
) -> jobjectArray {
|
|
||||||
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
|
||||||
let filter: Option<String> = if filter.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(
|
|
||||||
env.get_string(&filter)
|
|
||||||
.map(|s| s.into())
|
|
||||||
.jexcept(&mut env)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let file_tree = workspace.filetree(filter.as_deref(), strict != 0);
|
|
||||||
env.find_class("java/lang/String")
|
|
||||||
.and_then(|class| env.new_object_array(file_tree.len() as i32, class, JObject::null()))
|
|
||||||
.inspect(|arr| {
|
|
||||||
for (idx, path) in file_tree.iter().enumerate() {
|
|
||||||
env.new_string(path)
|
|
||||||
.and_then(|path| env.set_object_array_element(arr, idx as i32, path))
|
|
||||||
.jexcept(&mut env)
|
|
||||||
}
|
|
||||||
}).jexcept(&mut env).as_raw()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a list of the active buffers.
|
/// Gets a list of the active buffers.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Workspace")]
|
||||||
pub extern "system" fn Java_mp_code_Workspace_active_1buffers(
|
fn active_buffers(workspace: &mut Workspace) -> Vec<String> {
|
||||||
mut env: JNIEnv,
|
workspace.buffer_list()
|
||||||
_class: JClass,
|
|
||||||
self_ptr: jlong
|
|
||||||
) -> jobjectArray {
|
|
||||||
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
|
||||||
let active_buffer_list = workspace.buffer_list();
|
|
||||||
env.find_class("java/lang/String")
|
|
||||||
.and_then(|class| env.new_object_array(active_buffer_list.len() as i32, class, JObject::null()))
|
|
||||||
.inspect(|arr| {
|
|
||||||
for (idx, path) in active_buffer_list.iter().enumerate() {
|
|
||||||
env.new_string(path)
|
|
||||||
.and_then(|path| env.set_object_array_element(arr, idx as i32, path))
|
|
||||||
.jexcept(&mut env)
|
|
||||||
}
|
|
||||||
}).jexcept(&mut env).as_raw()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new buffer.
|
/// Create a new buffer.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Workspace")]
|
||||||
pub extern "system" fn Java_mp_code_Workspace_create_1buffer<'local>(
|
fn create_buffer(workspace: &mut Workspace, path: String) -> Result<(), RemoteError> {
|
||||||
mut env: JNIEnv,
|
super::tokio().block_on(workspace.create(&path))
|
||||||
_class: JClass<'local>,
|
|
||||||
self_ptr: jlong,
|
|
||||||
path: JString<'local>
|
|
||||||
) {
|
|
||||||
null_check!(env, path, {});
|
|
||||||
let ws = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
|
||||||
let path = unsafe { env.get_string_unchecked(&path) }
|
|
||||||
.map(|path| path.to_string_lossy().to_string())
|
|
||||||
.jexcept(&mut env);
|
|
||||||
super::tokio().block_on(ws.create(&path))
|
|
||||||
.jexcept(&mut env);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attach to a buffer and return a pointer to its [crate::buffer::Controller].
|
/// Attach to a buffer and return a pointer to its [crate::buffer::Controller].
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Workspace")]
|
||||||
pub extern "system" fn Java_mp_code_Workspace_attach_1to_1buffer<'local>(
|
fn attach_to_buffer(workspace: &mut Workspace, path: String) -> Result<crate::buffer::Controller, ConnectionError> {
|
||||||
mut env: JNIEnv,
|
|
||||||
_class: JClass<'local>,
|
|
||||||
self_ptr: jlong,
|
|
||||||
path: JString<'local>
|
|
||||||
) -> jobject {
|
|
||||||
null_check!(env, path, std::ptr::null_mut());
|
|
||||||
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
|
||||||
let path = unsafe { env.get_string_unchecked(&path) }
|
|
||||||
.map(|path| path.to_string_lossy().to_string())
|
|
||||||
.jexcept(&mut env);
|
|
||||||
super::tokio().block_on(workspace.attach(&path))
|
super::tokio().block_on(workspace.attach(&path))
|
||||||
.map(|buffer| buffer.jobjectify(&mut env).jexcept(&mut env))
|
|
||||||
.jexcept(&mut env)
|
|
||||||
.as_raw()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Detach from a buffer.
|
/// Detach from a buffer.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Workspace")]
|
||||||
pub extern "system" fn Java_mp_code_Workspace_detach_1from_1buffer<'local>(
|
fn detach_from_buffer(workspace: &mut Workspace, path: String) -> crate::workspace::DetachResult {
|
||||||
mut env: JNIEnv,
|
|
||||||
_class: JClass<'local>,
|
|
||||||
self_ptr: jlong,
|
|
||||||
path: JString<'local>
|
|
||||||
) -> jobject {
|
|
||||||
null_check!(env, path, std::ptr::null_mut());
|
|
||||||
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
|
||||||
let path = unsafe { env.get_string_unchecked(&path) }
|
|
||||||
.map(|path| path.to_string_lossy().to_string())
|
|
||||||
.jexcept(&mut env);
|
|
||||||
workspace.detach(&path)
|
workspace.detach(&path)
|
||||||
.jobjectify(&mut env)
|
|
||||||
.jexcept(&mut env)
|
|
||||||
.as_raw()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the local buffer list.
|
/// Update the local buffer list.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Workspace")]
|
||||||
pub extern "system" fn Java_mp_code_Workspace_fetch_1buffers(
|
fn fetch_buffers(workspace: &mut Workspace) -> Result<(), RemoteError> {
|
||||||
mut env: JNIEnv,
|
super::tokio().block_on(workspace.fetch_buffers())
|
||||||
_class: JClass,
|
|
||||||
self_ptr: jlong,
|
|
||||||
) {
|
|
||||||
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
|
||||||
super::tokio().block_on(workspace.fetch_buffers()).jexcept(&mut env);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the local user list.
|
/// Update the local user list.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Workspace")]
|
||||||
pub extern "system" fn Java_mp_code_Workspace_fetch_1users(
|
fn fetch_users(workspace: &mut Workspace) -> Result<(), RemoteError> {
|
||||||
mut env: JNIEnv,
|
super::tokio().block_on(workspace.fetch_users())
|
||||||
_class: JClass,
|
|
||||||
self_ptr: jlong,
|
|
||||||
) {
|
|
||||||
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
|
||||||
super::tokio().block_on(workspace.fetch_users()).jexcept(&mut env);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List users attached to a buffer.
|
/// List users attached to a buffer.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Workspace")]
|
||||||
pub extern "system" fn Java_mp_code_Workspace_list_1buffer_1users<'local>(
|
fn list_buffer_users(workspace: &mut Workspace, path: String) -> Result<Vec<crate::api::User>, RemoteError> {
|
||||||
mut env: JNIEnv,
|
super::tokio().block_on(workspace.list_buffer_users(&path))
|
||||||
_class: JClass<'local>,
|
|
||||||
self_ptr: jlong,
|
|
||||||
path: JString<'local>,
|
|
||||||
) -> jobjectArray {
|
|
||||||
null_check!(env, path, std::ptr::null_mut());
|
|
||||||
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
|
||||||
let buffer = unsafe { env.get_string_unchecked(&path) }
|
|
||||||
.map(|buffer| buffer.to_string_lossy().to_string())
|
|
||||||
.jexcept(&mut env);
|
|
||||||
let users = super::tokio().block_on(workspace.list_buffer_users(&buffer))
|
|
||||||
.jexcept(&mut env);
|
|
||||||
|
|
||||||
if env.exception_check().unwrap_or(false) { // prevent illegal state
|
|
||||||
return std::ptr::null_mut();
|
|
||||||
}
|
|
||||||
|
|
||||||
env.find_class("java/util/UUID")
|
|
||||||
.and_then(|class| env.new_object_array(users.len() as i32, &class, JObject::null()))
|
|
||||||
.inspect(|arr| {
|
|
||||||
for (idx, user) in users.iter().enumerate() {
|
|
||||||
user.id.jobjectify(&mut env)
|
|
||||||
.and_then(|id| env.set_object_array_element(arr, idx as i32, id))
|
|
||||||
.jexcept(&mut env);
|
|
||||||
}
|
|
||||||
}).jexcept(&mut env).as_raw()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete a buffer.
|
/// Delete a buffer.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Workspace")]
|
||||||
pub extern "system" fn Java_mp_code_Workspace_delete_1buffer<'local>(
|
fn delete_buffer(workspace: &mut Workspace, path: String) -> Result<(), RemoteError> {
|
||||||
mut env: JNIEnv,
|
super::tokio().block_on(workspace.delete(&path))
|
||||||
_class: JClass<'local>,
|
|
||||||
self_ptr: jlong,
|
|
||||||
path: JString<'local>,
|
|
||||||
) {
|
|
||||||
null_check!(env, path, {});
|
|
||||||
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
|
||||||
let buffer = unsafe { env.get_string_unchecked(&path) }
|
|
||||||
.map(|buffer| buffer.to_string_lossy().to_string())
|
|
||||||
.jexcept(&mut env);
|
|
||||||
super::tokio().block_on(workspace.delete(&buffer))
|
|
||||||
.jexcept(&mut env);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Receive a workspace event if present.
|
/// Receive a workspace event if present.
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Workspace")]
|
||||||
pub extern "system" fn Java_mp_code_Workspace_event(
|
fn event(workspace: &mut Workspace) -> Result<crate::api::Event, ControllerError> {
|
||||||
mut env: JNIEnv,
|
super::tokio().block_on(workspace.event())
|
||||||
_class: JClass,
|
|
||||||
self_ptr: jlong
|
|
||||||
) -> jobject {
|
|
||||||
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
|
||||||
let event = super::tokio().block_on(workspace.event());
|
|
||||||
if let Ok(event) = event {
|
|
||||||
event.jobjectify(&mut env).jexcept(&mut env).as_raw()
|
|
||||||
} else {
|
|
||||||
handle_error!(&mut env, event, std::ptr::null_mut())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called by the Java GC to drop a [Workspace].
|
/// Called by the Java GC to drop a [Workspace].
|
||||||
#[no_mangle]
|
#[jni(package = "mp.code", class = "Workspace")]
|
||||||
pub extern "system" fn Java_mp_code_Workspace_free(_env: JNIEnv, _class: JClass, input: jlong) {
|
fn free(input: jni::sys::jlong) {
|
||||||
let _ = unsafe { Box::from_raw(input as *mut Workspace) };
|
let _ = unsafe { Box::from_raw(input as *mut crate::Workspace) };
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue