codemp/src/ffi/java/workspace.rs

163 lines
5 KiB
Rust

use crate::{
api::controller::AsyncReceiver,
errors::{ConnectionError, ControllerError, RemoteError},
ffi::java::null_check,
Workspace,
};
use jni::{objects::JObject, JNIEnv};
use jni_toolbox::jni;
/// Get the workspace id.
#[jni(package = "mp.code", class = "Workspace")]
fn get_workspace_id(workspace: &mut Workspace) -> String {
workspace.id()
}
/// Get a cursor controller by name and returns a pointer to it.
#[jni(package = "mp.code", class = "Workspace")]
fn get_cursor(workspace: &mut Workspace) -> crate::cursor::Controller {
workspace.cursor()
}
/// Get a buffer controller by name and returns a pointer to it.
#[jni(package = "mp.code", class = "Workspace")]
fn get_buffer(workspace: &mut Workspace, path: String) -> Option<crate::buffer::Controller> {
workspace.buffer_by_name(&path)
}
/// Get the filetree.
#[jni(package = "mp.code", class = "Workspace")]
fn get_file_tree(workspace: &mut Workspace, filter: Option<String>, strict: bool) -> Vec<String> {
workspace.filetree(filter.as_deref(), strict)
}
/// Gets a list of the active buffers.
#[jni(package = "mp.code", class = "Workspace")]
fn active_buffers(workspace: &mut Workspace) -> Vec<String> {
workspace.buffer_list()
}
/// Gets a list of the active buffers.
#[jni(package = "mp.code", class = "Workspace")]
fn user_list(workspace: &mut Workspace) -> Vec<String> {
workspace.user_list()
}
/// Create a new buffer.
#[jni(package = "mp.code", class = "Workspace")]
fn create_buffer(workspace: &mut Workspace, path: String) -> Result<(), RemoteError> {
super::tokio().block_on(workspace.create(&path))
}
/// Attach to a buffer and return a pointer to its [crate::buffer::Controller].
#[jni(package = "mp.code", class = "Workspace")]
fn attach_to_buffer(
workspace: &mut Workspace,
path: String,
) -> Result<crate::buffer::Controller, ConnectionError> {
super::tokio().block_on(workspace.attach(&path))
}
/// Detach from a buffer.
#[jni(package = "mp.code", class = "Workspace")]
fn detach_from_buffer(workspace: &mut Workspace, path: String) -> bool {
workspace.detach(&path)
}
/// Update the local buffer list.
#[jni(package = "mp.code", class = "Workspace")]
fn fetch_buffers(workspace: &mut Workspace) -> Result<(), RemoteError> {
super::tokio().block_on(workspace.fetch_buffers())
}
/// Update the local user list.
#[jni(package = "mp.code", class = "Workspace")]
fn fetch_users(workspace: &mut Workspace) -> Result<(), RemoteError> {
super::tokio().block_on(workspace.fetch_users())
}
/// List users attached to a buffer.
#[jni(package = "mp.code", class = "Workspace")]
fn list_buffer_users(
workspace: &mut Workspace,
path: String,
) -> Result<Vec<crate::api::User>, RemoteError> {
super::tokio().block_on(workspace.list_buffer_users(&path))
}
/// Delete a buffer.
#[jni(package = "mp.code", class = "Workspace")]
fn delete_buffer(workspace: &mut Workspace, path: String) -> Result<(), RemoteError> {
super::tokio().block_on(workspace.delete(&path))
}
/// Block and receive a workspace event.
#[jni(package = "mp.code", class = "Workspace")]
fn recv(workspace: &mut Workspace) -> Result<crate::api::Event, ControllerError> {
super::tokio().block_on(workspace.recv())
}
/// Receive a workspace event if present.
#[jni(package = "mp.code", class = "Workspace")]
fn try_recv(workspace: &mut Workspace) -> Result<Option<crate::api::Event>, ControllerError> {
super::tokio().block_on(workspace.try_recv())
}
/// Block until a workspace event is available.
#[jni(package = "mp.code", class = "Workspace")]
fn poll(workspace: &mut Workspace) -> Result<(), ControllerError> {
super::tokio().block_on(workspace.poll())
}
/// Clear previously registered callback.
#[jni(package = "mp.code", class = "Workspace")]
fn clear_callback(workspace: &mut Workspace) {
workspace.clear_callback();
}
/// Register a callback for workspace events.
#[jni(package = "mp.code", class = "Workspace")]
fn callback<'local>(
env: &mut JNIEnv<'local>,
controller: &mut crate::Workspace,
cb: JObject<'local>,
) {
null_check!(env, cb, {});
let Ok(cb_ref) = env.new_global_ref(cb) else {
env.throw_new(
"mp/code/exceptions/JNIException",
"Failed to pin callback reference!",
)
.expect("Failed to throw exception!");
return;
};
controller.callback(move |workspace: crate::Workspace| {
let jvm = super::jvm();
let mut env = jvm
.attach_current_thread_permanently()
.expect("failed attaching to main JVM thread");
if let Err(e) = env.with_local_frame(5, |env| {
use jni_toolbox::IntoJavaObject;
let jworkspace = workspace.into_java_object(env)?;
if let Err(e) = env.call_method(
&cb_ref,
"accept",
"(Ljava/lang/Object;)V",
&[jni::objects::JValueGen::Object(&jworkspace)],
) {
tracing::error!("error invoking callback: {e:?}");
};
Ok::<(), jni::errors::Error>(())
}) {
tracing::error!("error invoking callback: {e}");
let _ = env.exception_describe();
}
});
}
/// Called by the Java GC to drop a [Workspace].
#[jni(package = "mp.code", class = "Workspace")]
fn free(input: jni::sys::jlong) {
let _ = unsafe { Box::from_raw(input as *mut crate::Workspace) };
}