mirror of
https://github.com/hexedtech/codemp.git
synced 2024-11-22 15:24:48 +01:00
feat(java): replace all expect/unwrap with throwing JNIException
This commit is contained in:
parent
53503ae117
commit
6bf541028d
7 changed files with 255 additions and 188 deletions
12
dist/java/src/mp/code/exceptions/JNIException.java
vendored
Normal file
12
dist/java/src/mp/code/exceptions/JNIException.java
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package mp.code.exceptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when an error happened in some jni-rs method that would've otherwise crashed
|
||||||
|
* the program. This way, the eventual crash can happen on the Java side.
|
||||||
|
* Only catch this if you are aware of the implications.
|
||||||
|
*/
|
||||||
|
public class JNIException extends RuntimeException {
|
||||||
|
public JNIException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,33 +2,33 @@ use jni::{objects::{JClass, JObject, JValueGen}, sys::{jlong, jobject, jstring},
|
||||||
|
|
||||||
use crate::api::Controller;
|
use crate::api::Controller;
|
||||||
|
|
||||||
use super::{util::JExceptable, RT};
|
use super::{JExceptable, RT};
|
||||||
|
|
||||||
/// Gets the name of the buffer.
|
/// Gets the name of the buffer.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "system" fn Java_mp_code_BufferController_get_1name(
|
pub extern "system" fn Java_mp_code_BufferController_get_1name(
|
||||||
env: JNIEnv,
|
mut env: JNIEnv,
|
||||||
_class: JClass,
|
_class: JClass,
|
||||||
self_ptr: jlong,
|
self_ptr: jlong,
|
||||||
) -> jstring {
|
) -> jstring {
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) };
|
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) };
|
||||||
let content = controller.name();
|
let content = controller.name();
|
||||||
env.new_string(content)
|
env.new_string(content)
|
||||||
.expect("could not create jstring")
|
.jexcept(&mut env)
|
||||||
.as_raw()
|
.as_raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the contents of the buffers.
|
/// Gets the contents of the buffers.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "system" fn Java_mp_code_BufferController_get_1content(
|
pub extern "system" fn Java_mp_code_BufferController_get_1content(
|
||||||
env: JNIEnv,
|
mut env: JNIEnv,
|
||||||
_class: JClass,
|
_class: JClass,
|
||||||
self_ptr: jlong,
|
self_ptr: jlong,
|
||||||
) -> jstring {
|
) -> jstring {
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) };
|
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) };
|
||||||
let content = controller.content();
|
let content = controller.content();
|
||||||
env.new_string(content)
|
env.new_string(content)
|
||||||
.expect("could not create jstring")
|
.jexcept(&mut env)
|
||||||
.as_raw()
|
.as_raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,20 +59,23 @@ pub extern "system" fn Java_mp_code_BufferController_recv(
|
||||||
/// Utility method to convert a [crate::api::TextChange] to its Java equivalent.
|
/// Utility method to convert a [crate::api::TextChange] to its Java equivalent.
|
||||||
fn recv_jni(env: &mut JNIEnv, change: Option<crate::api::TextChange>) -> jobject {
|
fn recv_jni(env: &mut JNIEnv, change: Option<crate::api::TextChange>) -> jobject {
|
||||||
match change {
|
match change {
|
||||||
None => JObject::null().as_raw(),
|
None => JObject::default(),
|
||||||
Some(event) => {
|
Some(event) => {
|
||||||
let class = env.find_class("mp/code/data/TextChange").expect("Couldn't find class!");
|
let content = env.new_string(event.content).jexcept(env);
|
||||||
env.new_object(
|
env.find_class("mp/code/data/TextChange")
|
||||||
class,
|
.and_then(|class| {
|
||||||
"(JJLjava/lang/String;)V",
|
env.new_object(
|
||||||
&[
|
class,
|
||||||
JValueGen::Long(jlong::from(event.start)),
|
"(JJLjava/lang/String;)V",
|
||||||
JValueGen::Long(jlong::from(event.end)),
|
&[
|
||||||
JValueGen::Object(&env.new_string(event.content).expect("Failed to create String!")),
|
JValueGen::Long(jlong::from(event.start)),
|
||||||
]
|
JValueGen::Long(jlong::from(event.end)),
|
||||||
).expect("failed creating object").into_raw()
|
JValueGen::Object(&content),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}).jexcept(env)
|
||||||
}
|
}
|
||||||
}
|
}.as_raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Receives from Java, converts and sends a [crate::api::TextChange].
|
/// Receives from Java, converts and sends a [crate::api::TextChange].
|
||||||
|
@ -83,14 +86,15 @@ pub extern "system" fn Java_mp_code_BufferController_send<'local>(
|
||||||
self_ptr: jlong,
|
self_ptr: jlong,
|
||||||
input: JObject<'local>
|
input: JObject<'local>
|
||||||
) {
|
) {
|
||||||
let start = env.get_field(&input, "start", "J").expect("could not get field").j().expect("field was not of expected type");
|
let start = env.get_field(&input, "start", "J").and_then(|s| s.j()).jexcept(&mut env);
|
||||||
let end = env.get_field(&input, "end", "J").expect("could not get field").j().expect("field was not of expected type");
|
let end = env.get_field(&input, "end", "J").and_then(|e| e.j()).jexcept(&mut env);
|
||||||
let content = env.get_field(&input, "content", "Ljava/lang/String;")
|
let content = env.get_field(&input, "content", "Ljava/lang/String;")
|
||||||
.expect("could not get field")
|
.and_then(|c| c.l())
|
||||||
.l()
|
.map(|c| c.into())
|
||||||
.expect("field was not of expected type")
|
.jexcept(&mut env);
|
||||||
.into();
|
let content = unsafe { env.get_string_unchecked(&content) }
|
||||||
let content = env.get_string(&content).expect("Failed to get String!").into();
|
.map(|c| c.to_string_lossy().to_string())
|
||||||
|
.jexcept(&mut env);
|
||||||
|
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) };
|
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) };
|
||||||
controller.send(crate::api::TextChange {
|
controller.send(crate::api::TextChange {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use jni::{objects::{JClass, JObject, JString, JValueGen}, sys::{jboolean, jlong, jobject}, JNIEnv};
|
use jni::{objects::{JClass, JString, JValueGen}, sys::{jboolean, jlong, jobject}, JNIEnv};
|
||||||
use crate::{client::Client, Workspace};
|
use crate::{client::Client, Workspace};
|
||||||
|
|
||||||
use super::{util::JExceptable, RT};
|
use super::{JExceptable, RT};
|
||||||
|
|
||||||
/// Connects to a given URL and returns a [Client] to interact with that server.
|
/// Connects to a given URL and returns a [Client] to interact with that server.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -12,15 +12,21 @@ pub extern "system" fn Java_mp_code_Client_connect<'local>(
|
||||||
user: JString<'local>,
|
user: JString<'local>,
|
||||||
pwd: JString<'local>
|
pwd: JString<'local>
|
||||||
) -> jobject {
|
) -> jobject {
|
||||||
let url: String = env.get_string(&url).expect("Couldn't get java string!").into();
|
let url: String = env.get_string(&url)
|
||||||
let user: String = env.get_string(&user).expect("Couldn't get java string!").into();
|
.map(|s| s.into())
|
||||||
let pwd: String = env.get_string(&pwd).expect("Couldn't get java string!").into();
|
.jexcept(&mut env);
|
||||||
|
let user: String = env.get_string(&user)
|
||||||
|
.map(|s| s.into())
|
||||||
|
.jexcept(&mut env);
|
||||||
|
let pwd: String = env.get_string(&pwd)
|
||||||
|
.map(|s| s.into())
|
||||||
|
.jexcept(&mut env);
|
||||||
RT.block_on(crate::Client::new(&url, &user, &pwd))
|
RT.block_on(crate::Client::new(&url, &user, &pwd))
|
||||||
.map(|client| Box::into_raw(Box::new(client)) as jlong)
|
.map(|client| Box::into_raw(Box::new(client)) as jlong)
|
||||||
.map(|ptr| {
|
.map(|ptr| {
|
||||||
let class = env.find_class("mp/code/Client").expect("Failed to find class");
|
env.find_class("mp/code/Client")
|
||||||
env.new_object(class, "(J)V", &[JValueGen::Long(ptr)])
|
.and_then(|class| env.new_object(class, "(J)V", &[JValueGen::Long(ptr)]))
|
||||||
.expect("Failed to initialise object")
|
.jexcept(&mut env)
|
||||||
}).jexcept(&mut env).as_raw()
|
}).jexcept(&mut env).as_raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,14 +39,16 @@ pub extern "system" fn Java_mp_code_Client_join_1workspace<'local>(
|
||||||
input: JString<'local>
|
input: JString<'local>
|
||||||
) -> jobject {
|
) -> jobject {
|
||||||
let client = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Client)) };
|
let client = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Client)) };
|
||||||
let workspace_id = unsafe { env.get_string_unchecked(&input).expect("Couldn't get java string!") };
|
let workspace_id = unsafe { env.get_string_unchecked(&input) }
|
||||||
RT.block_on(client.join_workspace(workspace_id.to_str().expect("Not UTF-8")))
|
.map(|wid| wid.to_string_lossy().to_string())
|
||||||
|
.jexcept(&mut env);
|
||||||
|
RT.block_on(client.join_workspace(workspace_id))
|
||||||
.map(|workspace| spawn_updater(workspace.clone()))
|
.map(|workspace| spawn_updater(workspace.clone()))
|
||||||
.map(|workspace| Box::into_raw(Box::new(workspace)) as jlong)
|
.map(|workspace| Box::into_raw(Box::new(workspace)) as jlong)
|
||||||
.map(|ptr| {
|
.map(|ptr| {
|
||||||
let class = env.find_class("mp/code/Workspace").expect("Failed to find class");
|
env.find_class("mp/code/Workspace")
|
||||||
env.new_object(class, "(J)V", &[JValueGen::Long(ptr)])
|
.and_then(|class| env.new_object(class, "(J)V", &[JValueGen::Long(ptr)]))
|
||||||
.expect("Failed to initialise object")
|
.jexcept(&mut env)
|
||||||
}).jexcept(&mut env).as_raw()
|
}).jexcept(&mut env).as_raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,14 +68,16 @@ fn spawn_updater(workspace: Workspace) -> Workspace {
|
||||||
/// Leaves a [Workspace] and returns whether or not the client was in such workspace.
|
/// Leaves a [Workspace] and returns whether or not the client was in such workspace.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "system" fn Java_mp_code_Client_leave_1workspace<'local>(
|
pub extern "system" fn Java_mp_code_Client_leave_1workspace<'local>(
|
||||||
env: JNIEnv<'local>,
|
mut env: JNIEnv<'local>,
|
||||||
_class: JClass<'local>,
|
_class: JClass<'local>,
|
||||||
self_ptr: jlong,
|
self_ptr: jlong,
|
||||||
input: JString<'local>
|
input: JString<'local>
|
||||||
) -> jboolean {
|
) -> jboolean {
|
||||||
let client = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Client)) };
|
let client = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Client)) };
|
||||||
let workspace_id = unsafe { env.get_string_unchecked(&input).expect("Couldn't get java string!") };
|
unsafe { env.get_string_unchecked(&input) }
|
||||||
client.leave_workspace(workspace_id.to_str().expect("Not UTF-8")) as jboolean
|
.map(|wid| wid.to_string_lossy().to_string())
|
||||||
|
.map(|wid| client.leave_workspace(&wid) as jboolean)
|
||||||
|
.jexcept(&mut env)
|
||||||
}
|
}
|
||||||
/// Gets a [Workspace] by name and returns a pointer to it.
|
/// Gets a [Workspace] by name and returns a pointer to it.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -78,14 +88,16 @@ pub extern "system" fn Java_mp_code_Client_get_1workspace<'local>(
|
||||||
input: JString<'local>
|
input: JString<'local>
|
||||||
) -> jobject {
|
) -> jobject {
|
||||||
let client = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Client)) };
|
let client = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Client)) };
|
||||||
let workspace_id = unsafe { env.get_string_unchecked(&input).expect("Couldn't get java string!") };
|
let workspace_id = unsafe { env.get_string_unchecked(&input) }
|
||||||
client.get_workspace(workspace_id.to_str().expect("Not UTF-8"))
|
.map(|wid| wid.to_string_lossy().to_string())
|
||||||
|
.jexcept(&mut env);
|
||||||
|
client.get_workspace(&workspace_id)
|
||||||
.map(|workspace| Box::into_raw(Box::new(workspace)) as jlong)
|
.map(|workspace| Box::into_raw(Box::new(workspace)) as jlong)
|
||||||
.map(|ptr| {
|
.map(|ptr| {
|
||||||
let class = env.find_class("mp/code/Workspace").expect("Failed to find class");
|
env.find_class("mp/code/Workspace")
|
||||||
env.new_object(class, "(J)V", &[JValueGen::Long(ptr)])
|
.and_then(|class| env.new_object(class, "(J)V", &[JValueGen::Long(ptr)]))
|
||||||
.expect("Failed to initialise object")
|
.jexcept(&mut env)
|
||||||
}).unwrap_or(JObject::null()).as_raw()
|
}).unwrap_or_default().as_raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets up the tracing subscriber.
|
/// Sets up the tracing subscriber.
|
||||||
|
@ -99,7 +111,7 @@ pub extern "system" fn Java_mp_code_Client_setup_1tracing<'local>(
|
||||||
true,
|
true,
|
||||||
Some(path)
|
Some(path)
|
||||||
.filter(|p| p.is_null())
|
.filter(|p| p.is_null())
|
||||||
.map(|p| env.get_string(&p).expect("couldn't get java string").into())
|
.map(|p| env.get_string(&p).map(|s| s.into()).jexcept(&mut env))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use jni::{objects::{JClass, JObject, JString, JValueGen}, sys::{jlong, jobject}, JNIEnv};
|
use jni::{objects::{JClass, JObject, JString, JValueGen}, sys::{jlong, jobject}, JNIEnv};
|
||||||
use crate::{api::Controller, ffi::java::util::JExceptable};
|
use crate::api::Controller;
|
||||||
|
|
||||||
use super::RT;
|
use super::{JExceptable, RT};
|
||||||
|
|
||||||
/// Tries to fetch a [crate::api::Cursor], or returns null if there's nothing.
|
/// Tries to fetch a [crate::api::Cursor], or returns null if there's nothing.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -30,23 +30,30 @@ pub extern "system" fn Java_mp_code_CursorController_recv(
|
||||||
/// Utility method to convert a [crate::api::Cursor] to its Java equivalent.
|
/// Utility method to convert a [crate::api::Cursor] to its Java equivalent.
|
||||||
fn jni_recv(env: &mut JNIEnv, cursor: Option<crate::api::Cursor>) -> jobject {
|
fn jni_recv(env: &mut JNIEnv, cursor: Option<crate::api::Cursor>) -> jobject {
|
||||||
match cursor {
|
match cursor {
|
||||||
None => JObject::null().as_raw(),
|
None => JObject::default(),
|
||||||
Some(event) => {
|
Some(event) => {
|
||||||
let class = env.find_class("mp/code/data/Cursor").expect("Couldn't find class!");
|
env.find_class("mp/code/data/Cursor")
|
||||||
env.new_object(
|
.and_then(|class| {
|
||||||
class,
|
let buffer = env.new_string(&event.buffer).jexcept(env);
|
||||||
"(IIIILjava/lang/String;Ljava/lang/String;)V",
|
let user = event.user
|
||||||
&[
|
.map(|uuid| uuid.to_string())
|
||||||
JValueGen::Int(event.start.0),
|
.map(|user| env.new_string(user).jexcept(env))
|
||||||
JValueGen::Int(event.start.1),
|
.unwrap_or_default();
|
||||||
JValueGen::Int(event.end.0),
|
env.new_object(
|
||||||
JValueGen::Int(event.end.1),
|
class,
|
||||||
JValueGen::Object(&env.new_string(event.buffer).expect("Failed to create String!")),
|
"(IIIILjava/lang/String;Ljava/lang/String;)V",
|
||||||
JValueGen::Object(&env.new_string(event.user.map(|x| x.to_string()).unwrap_or_default()).expect("Failed to create String!"))
|
&[
|
||||||
]
|
JValueGen::Int(event.start.0),
|
||||||
).expect("failed creating object").into_raw()
|
JValueGen::Int(event.start.1),
|
||||||
|
JValueGen::Int(event.end.0),
|
||||||
|
JValueGen::Int(event.end.1),
|
||||||
|
JValueGen::Object(&buffer),
|
||||||
|
JValueGen::Object(&user)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}).jexcept(env)
|
||||||
}
|
}
|
||||||
}
|
}.as_raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Receives from Java, converts and sends a [crate::api::Cursor].
|
/// Receives from Java, converts and sends a [crate::api::Cursor].
|
||||||
|
@ -57,28 +64,38 @@ pub extern "system" fn Java_mp_code_CursorController_send<'local>(
|
||||||
self_ptr: jlong,
|
self_ptr: jlong,
|
||||||
input: JObject<'local>,
|
input: JObject<'local>,
|
||||||
) {
|
) {
|
||||||
let start_row = env.get_field(&input, "startRow", "I").expect("could not get field").i().expect("field was not of expected type");
|
let start_row = env.get_field(&input, "startRow", "I")
|
||||||
let start_col = env.get_field(&input, "startCol", "I").expect("could not get field").i().expect("field was not of expected type");
|
.and_then(|sr| sr.i())
|
||||||
let end_row = env.get_field(&input, "endRow", "I").expect("could not get field").i().expect("field was not of expected type");
|
.jexcept(&mut env);
|
||||||
let end_col = env.get_field(&input, "endCol", "I").expect("could not get field").i().expect("field was not of expected type");
|
let start_col = env.get_field(&input, "startCol", "I")
|
||||||
|
.and_then(|sc| sc.i())
|
||||||
|
.jexcept(&mut env);
|
||||||
|
let end_row = env.get_field(&input, "endRow", "I")
|
||||||
|
.and_then(|er| er.i())
|
||||||
|
.jexcept(&mut env);
|
||||||
|
let end_col = env.get_field(&input, "endCol", "I")
|
||||||
|
.and_then(|ec| ec.i())
|
||||||
|
.jexcept(&mut env);
|
||||||
|
|
||||||
let buffer = env.get_field(&input, "buffer", "Ljava/lang/String;")
|
let buffer = env.get_field(&input, "buffer", "Ljava/lang/String;")
|
||||||
.expect("could not get field")
|
.and_then(|b| b.l())
|
||||||
.l()
|
.map(|b| b.into())
|
||||||
.expect("field was not of expected type")
|
.jexcept(&mut env);
|
||||||
.into();
|
let buffer = env.get_string(&buffer)
|
||||||
let buffer = env.get_string(&buffer).expect("Failed to get String!").into();
|
.map(|b| b.into())
|
||||||
|
.jexcept(&mut env);
|
||||||
|
|
||||||
let user: JString = env.get_field(&input, "user", "Ljava/lang/String;")
|
let user: JString = env.get_field(&input, "user", "Ljava/lang/String;")
|
||||||
.expect("could not get field")
|
.and_then(|u| u.l())
|
||||||
.l()
|
.map(|u| u.into())
|
||||||
.expect("field was not of expected type")
|
.jexcept(&mut env);
|
||||||
.into();
|
|
||||||
let user = if user.is_null() {
|
let user = if user.is_null() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let jstring = env.get_string(&user).expect("Failed to get String!");
|
let user: String = env.get_string(&user)
|
||||||
Some(uuid::Uuid::parse_str(jstring.to_str().expect("Not valid UTF-8")).expect("Invalid UUI!"))
|
.map(|u| u.into())
|
||||||
|
.jexcept(&mut env);
|
||||||
|
Some(uuid::Uuid::parse_str(&user).jexcept(&mut env))
|
||||||
};
|
};
|
||||||
|
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::cursor::Controller)) };
|
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::cursor::Controller)) };
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod workspace;
|
pub mod workspace;
|
||||||
pub mod util;
|
|
||||||
pub mod cursor;
|
pub mod cursor;
|
||||||
pub mod buffer;
|
pub mod buffer;
|
||||||
|
|
||||||
|
@ -34,3 +33,48 @@ pub(crate) fn setup_logger(debug: bool, path: Option<String>) {
|
||||||
builder.with_writer(std::sync::Mutex::new(std::io::stdout())).init();
|
builder.with_writer(std::sync::Mutex::new(std::io::stdout())).init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A trait meant for our [crate::Result] type to make converting it to Java easier.
|
||||||
|
/// jni-rs technically has [jni::errors::ToException], but this approach keeps it stream-like.
|
||||||
|
pub(crate) trait JExceptable<T> {
|
||||||
|
/// Unwraps it and throws an appropriate Java exception if it's an error.
|
||||||
|
/// Theoretically it returns the type's default value, but the exception makes the value ignored.
|
||||||
|
fn jexcept(self, env: &mut jni::JNIEnv) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> JExceptable<T> for crate::Result<T> where T: Default {
|
||||||
|
fn jexcept(self, env: &mut jni::JNIEnv) -> T {
|
||||||
|
if let Err(err) = &self {
|
||||||
|
let msg = format!("{err}");
|
||||||
|
match err {
|
||||||
|
crate::Error::InvalidState { .. } => env.throw_new("mp/code/exceptions/InvalidStateException", msg),
|
||||||
|
crate::Error::Deadlocked => env.throw_new("mp/code/exceptions/DeadlockedException", msg),
|
||||||
|
crate::Error::Transport { .. } => env.throw_new("mp/code/exceptions/TransportException", msg),
|
||||||
|
crate::Error::Channel { .. } => env.throw_new("mp/code/exceptions/ChannelException", msg)
|
||||||
|
}.jexcept(env);
|
||||||
|
}
|
||||||
|
self.unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> JExceptable<T> for Result<T, jni::errors::Error> where T: Default {
|
||||||
|
fn jexcept(self, env: &mut jni::JNIEnv) -> T {
|
||||||
|
if let Err(err) = &self {
|
||||||
|
let msg = format!("{err}");
|
||||||
|
env.throw_new("mp/code/exceptions/JNIException", msg)
|
||||||
|
.expect("A severe error occurred: we were unable to create a JNIException. This is an unrecoverable state.");
|
||||||
|
}
|
||||||
|
self.unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> JExceptable<T> for Result<T, uuid::Error> where T: Default {
|
||||||
|
fn jexcept(self, env: &mut jni::JNIEnv) -> T {
|
||||||
|
if let Err(err) = &self {
|
||||||
|
let msg = format!("{err}");
|
||||||
|
env.throw_new("java/lang/IllegalArgumentException", msg)
|
||||||
|
.expect("A severe error occurred: we were unable to create a JNIException. This is an unrecoverable state.");
|
||||||
|
}
|
||||||
|
self.unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
use jni::JNIEnv;
|
|
||||||
|
|
||||||
/// A trait meant for our [crate::Result] type to make converting it to Java easier.
|
|
||||||
pub(crate) trait JExceptable<T> {
|
|
||||||
/// Unwraps it and throws an appropriate Java exception if it's an error.
|
|
||||||
/// Theoretically it returns the type's default value, but the exception makes the value ignored.
|
|
||||||
fn jexcept(self, env: &mut JNIEnv) -> T;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> JExceptable<T> for crate::Result<T> where T: Default {
|
|
||||||
fn jexcept(self, env: &mut JNIEnv) -> T {
|
|
||||||
if let Err(err) = &self {
|
|
||||||
let msg = format!("{err}");
|
|
||||||
match err {
|
|
||||||
crate::Error::InvalidState { .. } => env.throw_new("mp/code/exceptions/InvalidStateException", msg),
|
|
||||||
crate::Error::Deadlocked => env.throw_new("mp/code/exceptions/DeadlockedException", msg),
|
|
||||||
crate::Error::Transport { .. } => env.throw_new("mp/code/exceptions/TransportException", msg),
|
|
||||||
crate::Error::Channel { .. } => env.throw_new("mp/code/exceptions/ChannelException", msg)
|
|
||||||
}.expect("Failed to throw exception!");
|
|
||||||
}
|
|
||||||
self.unwrap_or_default()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +1,17 @@
|
||||||
use jni::{objects::{JClass, JObject, JString, JValueGen}, sys::{jlong, jobject, jobjectArray, jstring}, JNIEnv};
|
use jni::{objects::{JClass, JObject, JString, JValueGen}, sys::{jlong, jobject, jobjectArray, jstring}, JNIEnv};
|
||||||
use crate::Workspace;
|
use crate::Workspace;
|
||||||
|
|
||||||
use super::{util::JExceptable, RT};
|
use super::{JExceptable, RT};
|
||||||
|
|
||||||
/// Gets the workspace id.
|
/// Gets the workspace id.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "system" fn Java_mp_code_Workspace_get_1workspace_1id<'local>(
|
pub extern "system" fn Java_mp_code_Workspace_get_1workspace_1id<'local>(
|
||||||
env: JNIEnv<'local>,
|
mut env: JNIEnv<'local>,
|
||||||
_class: JClass<'local>,
|
_class: JClass<'local>,
|
||||||
self_ptr: jlong
|
self_ptr: jlong
|
||||||
) -> jstring {
|
) -> jstring {
|
||||||
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
||||||
env.new_string(workspace.id())
|
env.new_string(workspace.id()).jexcept(&mut env).as_raw()
|
||||||
.expect("Failed to convert to Java String!")
|
|
||||||
.as_raw()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a cursor controller by name and returns a pointer to it.
|
/// Gets a cursor controller by name and returns a pointer to it.
|
||||||
|
@ -24,10 +22,9 @@ pub extern "system" fn Java_mp_code_Workspace_get_1cursor<'local>(
|
||||||
self_ptr: jlong
|
self_ptr: jlong
|
||||||
) -> jobject {
|
) -> jobject {
|
||||||
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
||||||
let class = env.find_class("mp/code/CursorController").expect("Failed to find class");
|
env.find_class("mp/code/CursorController").and_then(|class|
|
||||||
env.new_object(class, "(J)V", &[JValueGen::Long(Box::into_raw(Box::new(workspace.cursor())) as jlong)])
|
env.new_object(class, "(J)V", &[JValueGen::Long(Box::into_raw(Box::new(workspace.cursor())) as jlong)])
|
||||||
.expect("Failed to initialise object")
|
).jexcept(&mut env).as_raw()
|
||||||
.as_raw()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a buffer controller by name and returns a pointer to it.
|
/// Gets a buffer controller by name and returns a pointer to it.
|
||||||
|
@ -39,15 +36,15 @@ pub extern "system" fn Java_mp_code_Workspace_get_1buffer<'local>(
|
||||||
input: JString<'local>
|
input: JString<'local>
|
||||||
) -> jobject {
|
) -> jobject {
|
||||||
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
||||||
let path = unsafe { env.get_string_unchecked(&input).expect("Couldn't get java string!") };
|
let path = unsafe { env.get_string_unchecked(&input) }
|
||||||
if let Some(buf) = workspace.buffer_by_name(path.to_str().expect("Not UTF-8!")) {
|
.map(|path| path.to_string_lossy().to_string())
|
||||||
let class = env.find_class("mp/code/BufferController").expect("Failed to find class");
|
.jexcept(&mut env);
|
||||||
env.new_object(class, "(J)V", &[JValueGen::Long(Box::into_raw(Box::new(buf)) as jlong)])
|
|
||||||
.expect("Failed to initialise object")
|
workspace.buffer_by_name(&path).map(|buf| {
|
||||||
.as_raw()
|
env.find_class("mp/code/BufferController").and_then(|class|
|
||||||
} else {
|
env.new_object(class, "(J)V", &[JValueGen::Long(Box::into_raw(Box::new(buf)) as jlong)])
|
||||||
JObject::null().as_raw()
|
).jexcept(&mut env)
|
||||||
}
|
}).unwrap_or_default().as_raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new buffer.
|
/// Creates a new buffer.
|
||||||
|
@ -59,8 +56,11 @@ pub extern "system" fn Java_mp_code_Workspace_create_1buffer<'local>(
|
||||||
input: JString<'local>
|
input: JString<'local>
|
||||||
) {
|
) {
|
||||||
let ws = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
let ws = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
||||||
let path = unsafe { env.get_string_unchecked(&input).expect("Couldn't get java string!") };
|
let path = unsafe { env.get_string_unchecked(&input) }
|
||||||
RT.block_on(ws.create(path.to_str().expect("Not UTF-8"))).jexcept(&mut env);
|
.map(|path| path.to_string_lossy().to_string())
|
||||||
|
.jexcept(&mut env);
|
||||||
|
RT.block_on(ws.create(&path))
|
||||||
|
.jexcept(&mut env);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the filetree.
|
/// Gets the filetree.
|
||||||
|
@ -72,16 +72,16 @@ pub extern "system" fn Java_mp_code_Workspace_get_1file_1tree(
|
||||||
) -> jobjectArray {
|
) -> jobjectArray {
|
||||||
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
||||||
let file_tree = workspace.filetree();
|
let file_tree = workspace.filetree();
|
||||||
let class = env.find_class("java/lang/String").expect("Failed to find class!");
|
env.find_class("java/lang/String")
|
||||||
let arr = env.new_object_array(file_tree.len() as i32, class, JObject::null())
|
.and_then(|class| env.new_object_array(file_tree.len() as i32, class, JObject::null()))
|
||||||
.expect("failed creating array");
|
.map(|arr| {
|
||||||
for (idx, path) in file_tree.iter().enumerate() {
|
for (idx, path) in file_tree.iter().enumerate() {
|
||||||
let js = env.new_string(path).expect("Failed to create String!");
|
env.new_string(path)
|
||||||
env.set_object_array_element(&arr, idx as i32, js)
|
.and_then(|path| env.set_object_array_element(&arr, idx as i32, path))
|
||||||
.expect("Failed to set array element!")
|
.jexcept(&mut env)
|
||||||
}
|
}
|
||||||
|
arr
|
||||||
arr.as_raw()
|
}).jexcept(&mut env).as_raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attaches to a buffer and returns a pointer to its [crate::buffer::Controller].
|
/// Attaches to a buffer and returns a pointer to its [crate::buffer::Controller].
|
||||||
|
@ -93,13 +93,15 @@ pub extern "system" fn Java_mp_code_Workspace_attach_1to_1buffer<'local>(
|
||||||
input: JString<'local>
|
input: JString<'local>
|
||||||
) -> jobject {
|
) -> jobject {
|
||||||
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
||||||
let path = unsafe { env.get_string_unchecked(&input).expect("Couldn't get java string!") };
|
let path = unsafe { env.get_string_unchecked(&input) }
|
||||||
RT.block_on(workspace.attach(path.to_str().expect("Not UTF-8!")))
|
.map(|path| path.to_string_lossy().to_string())
|
||||||
|
.jexcept(&mut env);
|
||||||
|
RT.block_on(workspace.attach(&path))
|
||||||
.map(|buffer| Box::into_raw(Box::new(buffer)) as jlong)
|
.map(|buffer| Box::into_raw(Box::new(buffer)) as jlong)
|
||||||
.map(|ptr| {
|
.map(|ptr| {
|
||||||
let class = env.find_class("mp/code/BufferController").expect("Failed to find class");
|
env.find_class("mp/code/BufferController")
|
||||||
env.new_object(class, "(J)V", &[JValueGen::Long(ptr)])
|
.and_then(|class| env.new_object(class, "(J)V", &[JValueGen::Long(ptr)]))
|
||||||
.expect("Failed to initialise object")
|
.jexcept(&mut env)
|
||||||
}).jexcept(&mut env).as_raw()
|
}).jexcept(&mut env).as_raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,18 +113,19 @@ pub extern "system" fn Java_mp_code_Workspace_detach_1from_1buffer<'local>(
|
||||||
input: JString<'local>
|
input: JString<'local>
|
||||||
) -> jobject {
|
) -> jobject {
|
||||||
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
||||||
let path = unsafe { env.get_string_unchecked(&input).expect("Couldn't get java string!") };
|
let path = unsafe { env.get_string_unchecked(&input) }
|
||||||
let name = match workspace.detach(path.to_str().expect("Not UTF-8")) {
|
.map(|path| path.to_string_lossy().to_string())
|
||||||
|
.jexcept(&mut env);
|
||||||
|
let name = match workspace.detach(&path) {
|
||||||
crate::workspace::worker::DetachResult::NotAttached => "NOT_ATTACHED",
|
crate::workspace::worker::DetachResult::NotAttached => "NOT_ATTACHED",
|
||||||
crate::workspace::worker::DetachResult::Detaching => "DETACHED",
|
crate::workspace::worker::DetachResult::Detaching => "DETACHED",
|
||||||
crate::workspace::worker::DetachResult::AlreadyDetached => "ALREADY_DETACHED"
|
crate::workspace::worker::DetachResult::AlreadyDetached => "ALREADY_DETACHED"
|
||||||
};
|
};
|
||||||
|
|
||||||
let class = env.find_class("mp/code/data/DetachResult").expect("Failed to find class!");
|
env.find_class("mp/code/data/DetachResult")
|
||||||
env.get_static_field(class, name, "Lmp/code/data/DetachResult;")
|
.and_then(|class| env.get_static_field(class, name, "Lmp/code/data/DetachResult;"))
|
||||||
.expect("Failed to get field!")
|
.and_then(|res| res.l())
|
||||||
.l()
|
.jexcept(&mut env)
|
||||||
.expect("Field was of wrong type!")
|
|
||||||
.as_raw()
|
.as_raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,21 +160,22 @@ pub extern "system" fn Java_mp_code_Workspace_list_1buffer_1users<'local>(
|
||||||
input: JString<'local>,
|
input: JString<'local>,
|
||||||
) -> jobjectArray {
|
) -> jobjectArray {
|
||||||
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
||||||
let buffer = unsafe { env.get_string_unchecked(&input).expect("Couldn't get java string!") };
|
let buffer = unsafe { env.get_string_unchecked(&input) }
|
||||||
let users = RT.block_on(workspace.list_buffer_users(buffer.to_str().expect("Not UTF-8!")))
|
.map(|buffer| buffer.to_string_lossy().to_string())
|
||||||
|
.jexcept(&mut env);
|
||||||
|
let users = RT.block_on(workspace.list_buffer_users(&buffer))
|
||||||
.jexcept(&mut env);
|
.jexcept(&mut env);
|
||||||
|
|
||||||
let class = env.find_class("java/lang/String").expect("Failed to find class!");
|
env.find_class("java/lang/String")
|
||||||
let arr = env.new_object_array(users.len() as i32, class, JObject::null())
|
.and_then(|class| env.new_object_array(users.len() as i32, class, JObject::null()))
|
||||||
.expect("failed creating array");
|
.map(|arr| {
|
||||||
|
for (idx, user) in users.iter().enumerate() {
|
||||||
for (idx, user) in users.iter().enumerate() {
|
env.new_string(&user.id)
|
||||||
let js = env.new_string(&user.id).expect("Failed to create String!");
|
.and_then(|id| env.set_object_array_element(&arr, idx as i32, id))
|
||||||
env.set_object_array_element(&arr, idx as i32, js)
|
.jexcept(&mut env);
|
||||||
.expect("Failed to set array element!")
|
}
|
||||||
}
|
arr
|
||||||
|
}).jexcept(&mut env).as_raw()
|
||||||
arr.as_raw()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deletes a buffer.
|
/// Deletes a buffer.
|
||||||
|
@ -182,9 +186,11 @@ pub extern "system" fn Java_mp_code_Workspace_delete_1buffer<'local>(
|
||||||
self_ptr: jlong,
|
self_ptr: jlong,
|
||||||
input: JString<'local>,
|
input: JString<'local>,
|
||||||
) {
|
) {
|
||||||
let buffer = unsafe { env.get_string_unchecked(&input).expect("Couldn't get java string!") };
|
|
||||||
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
||||||
RT.block_on(workspace.delete(buffer.to_str().expect("Not UTF-8!"))).jexcept(&mut env);
|
let buffer = unsafe { env.get_string_unchecked(&input) }
|
||||||
|
.map(|buffer| buffer.to_string_lossy().to_string())
|
||||||
|
.jexcept(&mut env);
|
||||||
|
RT.block_on(workspace.delete(&buffer)).jexcept(&mut env);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Receives a workspace event if present.
|
/// Receives a workspace event if present.
|
||||||
|
@ -197,29 +203,28 @@ pub extern "system" fn Java_mp_code_Workspace_event(
|
||||||
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
||||||
RT.block_on(workspace.event())
|
RT.block_on(workspace.event())
|
||||||
.map(|event| {
|
.map(|event| {
|
||||||
let type_class = env.find_class("mp/code/Workspace$Event$Type").expect("Failed to find class!");
|
|
||||||
let (name, arg) = match event {
|
let (name, arg) = match event {
|
||||||
crate::api::Event::FileTreeUpdated => ("FILE_TREE_UPDATED", None),
|
crate::api::Event::FileTreeUpdated => ("FILE_TREE_UPDATED", None),
|
||||||
crate::api::Event::UserJoin(arg) => ("USER_JOIN", Some(arg)),
|
crate::api::Event::UserJoin(arg) => ("USER_JOIN", Some(arg)),
|
||||||
crate::api::Event::UserLeave(arg) => ("USER_LEAVE", Some(arg)),
|
crate::api::Event::UserLeave(arg) => ("USER_LEAVE", Some(arg)),
|
||||||
};
|
};
|
||||||
let event_type = env.get_static_field(type_class, name, "Lmp/code/Workspace/Event/Type;")
|
let event_type = env.find_class("mp/code/Workspace$Event$Type")
|
||||||
.expect("Failed to get field!")
|
.and_then(|class| env.get_static_field(class, name, "Lmp/code/Workspace/Event/Type;"))
|
||||||
.l()
|
.and_then(|f| f.l())
|
||||||
.expect("Field was not of expected type!");
|
.jexcept(&mut env);
|
||||||
|
let arg = arg.map(|s| env.new_string(s).jexcept(&mut env))
|
||||||
let arg = arg.map(|s| env.new_string(s).expect("Failed to create String!"))
|
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let event_class = env.find_class("mp/code/Workspace$Event").expect("Failed to find class!");
|
env.find_class("mp/code/Workspace$Event").and_then(|class|
|
||||||
env.new_object(
|
env.new_object(
|
||||||
event_class,
|
class,
|
||||||
"(Lmp/code/Workspace/Event/Type;Ljava/lang/String;)V",
|
"(Lmp/code/Workspace/Event/Type;Ljava/lang/String;)V",
|
||||||
&[
|
&[
|
||||||
JValueGen::Object(&event_type),
|
JValueGen::Object(&event_type),
|
||||||
JValueGen::Object(&arg)
|
JValueGen::Object(&arg)
|
||||||
]
|
]
|
||||||
).expect("Failed to create object!")
|
)
|
||||||
|
).jexcept(&mut env)
|
||||||
}).jexcept(&mut env).as_raw()
|
}).jexcept(&mut env).as_raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,20 +245,16 @@ pub extern "system" fn Java_mp_code_Workspace_select_1buffer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let active = RT.block_on(crate::buffer::tools::select_buffer(
|
RT.block_on(crate::buffer::tools::select_buffer(
|
||||||
&controllers,
|
&controllers,
|
||||||
Some(std::time::Duration::from_millis(timeout as u64)),
|
Some(std::time::Duration::from_millis(timeout as u64)),
|
||||||
&RT,
|
&RT,
|
||||||
)).jexcept(&mut env);
|
)).jexcept(&mut env)
|
||||||
|
.map(|buf| {
|
||||||
if let Some(buf) = active {
|
env.find_class("mp/code/BufferController").and_then(|class|
|
||||||
let class = env.find_class("mp/code/BufferController").expect("Failed to find class");
|
env.new_object(class, "(J)V", &[JValueGen::Long(Box::into_raw(Box::new(buf)) as jlong)])
|
||||||
env.new_object(class, "(J)V", &[JValueGen::Long(Box::into_raw(Box::new(buf)) as jlong)])
|
).jexcept(&mut env)
|
||||||
.expect("Failed to initialise object")
|
}).unwrap_or_default().as_raw()
|
||||||
.as_raw()
|
|
||||||
} else {
|
|
||||||
JObject::null().as_raw()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called by the Java GC to drop a [Workspace].
|
/// Called by the Java GC to drop a [Workspace].
|
||||||
|
|
Loading…
Reference in a new issue