mirror of
https://github.com/hexedtech/codemp.git
synced 2024-11-21 14:54:49 +01:00
feat: add jni-toolbox, convert client
This commit is contained in:
parent
9218e9e9c7
commit
7056dc341c
5 changed files with 149 additions and 240 deletions
22
Cargo.lock
generated
22
Cargo.lock
generated
|
@ -238,6 +238,7 @@ dependencies = [
|
|||
"dashmap",
|
||||
"diamond-types",
|
||||
"jni",
|
||||
"jni-toolbox",
|
||||
"lazy_static",
|
||||
"mlua-codemp-patch",
|
||||
"napi",
|
||||
|
@ -746,6 +747,27 @@ version = "0.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
||||
|
||||
[[package]]
|
||||
name = "jni-toolbox"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40c14725e4fd3fe9ea885cea9df1ff1fa9f4d45a6f4253f6b90802f0941335f6"
|
||||
dependencies = [
|
||||
"jni",
|
||||
"jni-toolbox-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jni-toolbox-macro"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9970cad895bb316f70956593710d675d27a480ddbb8099f7e313042463a16d9b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.70"
|
||||
|
|
|
@ -41,6 +41,7 @@ tracing-subscriber = { version = "0.3", optional = true }
|
|||
# glue (java)
|
||||
lazy_static = { version = "1.5", optional = true }
|
||||
jni = { version = "0.21", features = ["invocation"], optional = true }
|
||||
jni-toolbox = { version = "0.1.3", optional = true }
|
||||
|
||||
# glue (lua)
|
||||
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"]
|
||||
# ffi
|
||||
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"]
|
||||
py = ["pyo3", "tracing-subscriber", "pyo3-build-config"]
|
||||
lua = ["mlua-codemp-patch", "tracing-subscriber", "lazy_static", "serialize"]
|
||||
|
|
|
@ -1,166 +1,133 @@
|
|||
use jni::{objects::{JClass, JObject, JString}, sys::{jboolean, jlong, jobject, jobjectArray}, JNIEnv};
|
||||
use crate::{api::Config, client::Client, ffi::java::{handle_error, null_check}, Workspace};
|
||||
use jni_toolbox::{jni, FromJava, IntoJava, JniToolboxError};
|
||||
use crate::{api::Config, client::Client, errors::{ConnectionError, RemoteError}, ffi::java::{handle_error, null_check}, Workspace};
|
||||
|
||||
use super::{Deobjectify, JExceptable, JObjectify, tokio};
|
||||
|
||||
/// Connect using the given credentials to the default server, and return a [Client] to interact with it.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_mp_code_Client_connect<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
_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());
|
||||
}
|
||||
impl<'j> IntoJava<'j> for Client {
|
||||
type T = jobject;
|
||||
|
||||
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());
|
||||
fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result<Self::T, jni::errors::Error> {
|
||||
// Ok(Box::into_raw(Box::new(self)))
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'j> FromJava<'j> for Client {
|
||||
type T = jobject;
|
||||
|
||||
fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::T) -> Result<Self, jni::errors::Error> {
|
||||
let x = unsafe { Box::leak(Box::from_raw(value as *mut Client)) };
|
||||
todo!();
|
||||
Ok(x.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'j> FromJava<'j> for Config {
|
||||
type T = JObject<'j>;
|
||||
fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::T) -> Result<Self, jni::errors::Error> {
|
||||
Ok(Config::deobjectify(env, value)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'j> IntoJava<'j> for crate::api::User {
|
||||
type T = jobject;
|
||||
fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result<Self::T, jni::errors::Error> {
|
||||
Ok(self.jobjectify(env)?.into_raw())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'j> IntoJava<'j> for Workspace {
|
||||
type T = jobject;
|
||||
fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result<Self::T, jni::errors::Error> {
|
||||
Ok(self.jobjectify(env)?.into_raw())
|
||||
}
|
||||
}
|
||||
|
||||
impl JniToolboxError for ConnectionError {
|
||||
fn jclass(&self) -> String { // TODO pick class based on underlying type
|
||||
"mp/code/exceptions/ConnectionRemoteException".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl JniToolboxError for RemoteError {
|
||||
fn jclass(&self) -> String { // TODO pick class based on underlying type
|
||||
"mp/code/exceptions/ConnectionRemoteException".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[jni(package = "mp.code", class = "Client", ptr)]
|
||||
fn connect(config: Config) -> Result<Client, ConnectionError> {
|
||||
tokio().block_on(Client::connect(config))
|
||||
}
|
||||
|
||||
fn asd(arg: String) -> Result<Vec<String>, String> {
|
||||
Ok(arg.split('/').map(|x| x.to_string()).collect())
|
||||
}
|
||||
|
||||
/// Gets the current [crate::api::User].
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_mp_code_Client_get_1user(
|
||||
mut env: JNIEnv,
|
||||
_class: JClass,
|
||||
self_ptr: jlong
|
||||
) -> jobject {
|
||||
let client = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Client)) };
|
||||
#[jni(package = "mp.code", class = "Client", ptr)]
|
||||
fn get_user(client: Client) -> crate::api::User {
|
||||
client.user().clone()
|
||||
.jobjectify(&mut env)
|
||||
.jexcept(&mut env)
|
||||
.as_raw()
|
||||
}
|
||||
|
||||
/// Join a [Workspace] and return a pointer to it.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_mp_code_Client_join_1workspace<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
_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())
|
||||
}
|
||||
#[jni(package = "mp.code", class = "Client", ptr)]
|
||||
fn join_workspace(client: Client, workspace: String) -> Result<Workspace, ConnectionError> {
|
||||
tokio().block_on(client.join_workspace(workspace))
|
||||
}
|
||||
|
||||
/// Create a workspace on server, if allowed to.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_mp_code_Client_create_1workspace<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
_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);
|
||||
#[jni(package = "mp.code", class = "Client")]
|
||||
fn create_workspace(client: Client, workspace: String) -> Result<(), RemoteError> {
|
||||
tokio().block_on(client.create_workspace(workspace))
|
||||
}
|
||||
|
||||
/// Delete a workspace on server, if allowed to.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_mp_code_Client_delete_1workspace<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
_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);
|
||||
#[jni(package = "mp.code", class = "Client")]
|
||||
fn delete_workspace(client: Client, workspace: String) -> Result<(), RemoteError> {
|
||||
tokio().block_on(client.delete_workspace(workspace))
|
||||
}
|
||||
|
||||
/// Invite another user to an owned workspace.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_mp_code_Client_invite_1to_1workspace<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
_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);
|
||||
#[jni(package = "mp.code", class = "Client")]
|
||||
fn invite_to_workspace(client: Client, workspace: String, user: String) -> Result<(), RemoteError> {
|
||||
tokio().block_on(client.invite_to_workspace(workspace, user))
|
||||
}
|
||||
|
||||
/// List available workspaces.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_mp_code_Client_list_1workspaces<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
_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()
|
||||
#[jni(package = "mp.code", class = "Client", ptr)]
|
||||
fn list_workspaces(client: Client, owned: bool, invited: bool) -> Result<Vec<String>, RemoteError> {
|
||||
tokio().block_on(client.list_workspaces(owned, invited))
|
||||
}
|
||||
|
||||
/// List available workspaces.
|
||||
#[jni(package = "mp.code", class = "Client", ptr)]
|
||||
fn active_workspaces(client: Client) -> Vec<String> {
|
||||
client.active_workspaces()
|
||||
}
|
||||
|
||||
/// Leave a [Workspace] and return whether or not the client was in such workspace.
|
||||
#[jni(package = "mp.code", class = "Client")]
|
||||
fn leave_workspace(client: Client, workspace: String) -> bool {
|
||||
client.leave_workspace(&workspace)
|
||||
}
|
||||
|
||||
/// Get a [Workspace] by name and returns a pointer to it.
|
||||
#[jni(package = "mp.code", class = "Client", ptr)]
|
||||
fn get_workspace(client: Client, workspace: String) -> Option<Workspace> {
|
||||
client.get_workspace(&workspace)
|
||||
}
|
||||
|
||||
/// Refresh the client's session token.
|
||||
#[jni(package = "mp.code", class = "Client")]
|
||||
fn refresh(client: Client) -> Result<(), RemoteError> {
|
||||
tokio().block_on(client.refresh())
|
||||
}
|
||||
|
||||
/// Called by the Java GC to drop a [Client].
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_mp_code_Client_active_1workspaces<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
_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()
|
||||
pub extern "system" fn Java_mp_code_Client_free(_env: JNIEnv, _class: JClass, input: jlong) {
|
||||
let _ = unsafe { Box::from_raw(input as *mut Client) };
|
||||
}
|
||||
|
||||
// TODO: this stays until we get rid of the arc then i'll have to find a better way
|
||||
|
@ -175,57 +142,3 @@ fn spawn_updater(workspace: Workspace) -> Workspace {
|
|||
});
|
||||
workspace
|
||||
}
|
||||
|
||||
/// Leave a [Workspace] and return whether or not the client was in such workspace.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_mp_code_Client_leave_1workspace<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
_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.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_mp_code_Client_get_1workspace<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
_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.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_mp_code_Client_refresh<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
_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].
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_mp_code_Client_free(_env: JNIEnv, _class: JClass, input: jlong) {
|
||||
let _ = unsafe { Box::from_raw(input as *mut Client) };
|
||||
}
|
||||
|
|
|
@ -1,30 +1,19 @@
|
|||
use jni::{objects::{JClass, JString}, sys::{jboolean, jlong}, JNIEnv};
|
||||
use jni::{objects::{JClass, JString}, JNIEnv};
|
||||
use jni_toolbox::jni;
|
||||
|
||||
use super::{JExceptable, null_check};
|
||||
|
||||
/// Calculate the XXH3 hash for a given String.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_mp_code_Extensions_hash<'local>(
|
||||
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);
|
||||
#[jni(package = "mp.code", class = "Extensions")]
|
||||
fn hash(content: String) -> i64 {
|
||||
let hash = crate::ext::hash(content.as_bytes());
|
||||
i64::from_ne_bytes(hash.to_ne_bytes())
|
||||
}
|
||||
|
||||
/// Tells the [tokio] runtime how to drive the event loop.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_mp_code_Extensions_drive(
|
||||
_env: JNIEnv,
|
||||
_class: JClass,
|
||||
block: jboolean
|
||||
) {
|
||||
if block != 0 {
|
||||
#[jni(package = "mp.code", class = "Extensions")]
|
||||
fn drive(block: bool) {
|
||||
if block {
|
||||
super::tokio().block_on(std::future::pending::<()>());
|
||||
} else {
|
||||
std::thread::spawn(|| {
|
||||
|
@ -34,19 +23,7 @@ pub extern "system" fn Java_mp_code_Extensions_drive(
|
|||
}
|
||||
|
||||
/// Set up the tracing subscriber.
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
pub extern "system" fn Java_mp_code_Extensions_setupTracing<'local>(
|
||||
mut env: JNIEnv,
|
||||
_class: JClass<'local>,
|
||||
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))
|
||||
);
|
||||
#[jni(package = "mp.code", class = "Extensions")]
|
||||
fn setupTracing(path: Option<String>, debug: bool) {
|
||||
super::setup_logger(debug, path);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use jni::{objects::{JClass, JObject, JString}, sys::{jboolean, jlong, jobject, jobjectArray, jstring}, JNIEnv};
|
||||
use jni_toolbox::jni;
|
||||
use crate::Workspace;
|
||||
|
||||
use super::{handle_error, null_check, JExceptable, JObjectify};
|
||||
|
@ -45,35 +46,30 @@ pub extern "system" fn Java_mp_code_Workspace_get_1buffer<'local>(
|
|||
}
|
||||
|
||||
/// Get the filetree.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_mp_code_Workspace_get_1file_1tree(
|
||||
#[jni(package = "mp.code", class = "Workspace", ptr)]
|
||||
fn file_tree(
|
||||
mut env: JNIEnv,
|
||||
_class: JClass,
|
||||
self_ptr: jlong,
|
||||
filter: JString,
|
||||
strict: jboolean
|
||||
) -> jobjectArray {
|
||||
) -> Result<jobjectArray, jni::errors::Error> {
|
||||
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)
|
||||
)
|
||||
Some(env.get_string(&filter)?.into())
|
||||
};
|
||||
|
||||
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()
|
||||
let class = env.find_class("java/lang/String")?;
|
||||
let array = env.new_object_array(file_tree.len() as i32, class, JObject::null())?;
|
||||
for (idx, path) in file_tree.iter().enumerate() {
|
||||
let element = env.new_string(path)?;
|
||||
env.set_object_array_element(&array, idx as i32, element)?;
|
||||
}
|
||||
|
||||
Ok(array.as_raw())
|
||||
}
|
||||
|
||||
/// Gets a list of the active buffers.
|
||||
|
|
Loading…
Reference in a new issue