use jni::errors::ErrorPolicy;
use jni::objects::{JByteArray, JClass, JObject, JObjectArray, JString};
use jni::strings::JNIString;
use jni::sys::{jbyteArray, jlong, jobjectArray, jsize};
use jni::{Env, EnvUnowned};
use crate::builder::JSONTools;
use crate::config_json::build_tools;
use crate::error::JsonToolsError;
const EXCEPTION_CLASS: &str = "io/github/amaye15/jsontoolsrs/JsonToolsException";
fn throw(env: &mut Env, message: &str) {
let _ = env.throw_new(JNIString::from(EXCEPTION_CLASS), JNIString::from(message));
}
fn panic_message(panic: &(dyn std::any::Any + Send)) -> String {
if let Some(s) = panic.downcast_ref::<&str>() {
(*s).to_string()
} else if let Some(s) = panic.downcast_ref::<String>() {
s.clone()
} else {
"unknown panic".to_string()
}
}
struct ThrowJsonToolsException;
impl<T: Default> ErrorPolicy<T, JsonToolsError> for ThrowJsonToolsException {
type Captures<'unowned_env_local, 'native_method>
= ()
where
'unowned_env_local: 'native_method;
fn on_error<'unowned_env_local, 'native_method>(
env: &mut Env<'unowned_env_local>,
_cap: &mut Self::Captures<'unowned_env_local, 'native_method>,
err: JsonToolsError,
) -> jni::errors::Result<T>
where
'unowned_env_local: 'native_method,
{
throw(env, &err.to_string());
Ok(T::default())
}
fn on_panic<'unowned_env_local, 'native_method>(
env: &mut Env<'unowned_env_local>,
_cap: &mut Self::Captures<'unowned_env_local, 'native_method>,
payload: Box<dyn std::any::Any + Send + 'static>,
) -> jni::errors::Result<T>
where
'unowned_env_local: 'native_method,
{
let message = panic_message(&*payload);
throw(env, &format!("internal panic in native code: {message}"));
Ok(T::default())
}
}
#[allow(non_snake_case)]
#[no_mangle]
pub extern "system" fn Java_io_github_amaye15_jsontoolsrs_JsonToolsNative_nativeCreate<'local>(
mut env: EnvUnowned<'local>,
_class: JClass<'local>,
config_json: JString<'local>,
) -> jlong {
env.with_env(|env| -> Result<jlong, JsonToolsError> {
let config_str = config_json.try_to_string(env)?;
let tools = build_tools(&config_str)?;
Ok(Box::into_raw(Box::new(tools)) as jlong)
})
.resolve::<ThrowJsonToolsException>()
}
#[allow(non_snake_case)]
#[no_mangle]
pub extern "system" fn Java_io_github_amaye15_jsontoolsrs_JsonToolsNative_nativeExecuteBytes<
'local,
>(
mut env: EnvUnowned<'local>,
_class: JClass<'local>,
handle: jlong,
json: JByteArray<'local>,
) -> jbyteArray {
env.with_env(|env| -> Result<jbyteArray, JsonToolsError> {
let tools = unsafe { &*(handle as *const JSONTools) };
let bytes = env.convert_byte_array(&json)?;
let json_str = String::from_utf8(bytes).map_err(|e| {
JsonToolsError::input_validation_error(format!("input is not valid UTF-8: {e}"))
})?;
let result = tools.execute(json_str.as_str())?.try_into_single()?;
Ok(env.byte_array_from_slice(result.as_bytes())?.into_raw())
})
.resolve::<ThrowJsonToolsException>()
}
#[allow(non_snake_case)]
#[no_mangle]
pub extern "system" fn Java_io_github_amaye15_jsontoolsrs_JsonToolsNative_nativeExecuteBatchBytes<
'local,
>(
mut env: EnvUnowned<'local>,
_class: JClass<'local>,
handle: jlong,
json_array: JObjectArray<'local, JByteArray<'local>>,
) -> jobjectArray {
env.with_env(|env| -> Result<jobjectArray, JsonToolsError> {
let tools = unsafe { &*(handle as *const JSONTools) };
let len = json_array.len(env)?;
env.ensure_local_capacity(2 * len + 16)?;
let mut inputs: Vec<String> = Vec::with_capacity(len);
for i in 0..len {
let jarr = json_array.get_element(env, i)?;
let bytes = env.convert_byte_array(&jarr)?;
inputs.push(String::from_utf8(bytes).map_err(|e| {
JsonToolsError::input_validation_error(format!("input is not valid UTF-8: {e}"))
})?);
}
let results = tools.execute(inputs)?.try_into_multiple()?;
let out_array = env.new_object_array(
results.len() as jsize,
JNIString::from("[B"),
JObject::null(),
)?;
for (i, s) in results.into_iter().enumerate() {
let jb = env.byte_array_from_slice(s.as_bytes())?;
out_array.set_element(env, i, &jb)?;
}
Ok(out_array.into_raw())
})
.resolve::<ThrowJsonToolsException>()
}
#[allow(non_snake_case)]
#[no_mangle]
pub extern "system" fn Java_io_github_amaye15_jsontoolsrs_JsonToolsNative_nativeDestroy<'local>(
mut env: EnvUnowned<'local>,
_class: JClass<'local>,
handle: jlong,
) {
env.with_env(|_env| -> Result<(), JsonToolsError> {
if handle != 0 {
unsafe {
drop(Box::from_raw(handle as *mut JSONTools));
}
}
Ok(())
})
.resolve::<ThrowJsonToolsException>()
}