# jni-toolbox this is a simple crate built around [jni-rs](https://github.com/jni-rs/jni-rs) to automatically generate JNI-compatible extern functions it also wraps functions returning `Result<>`, making short-circuiting easy ## usage just specify package and class on your function, and done! ```rust #[jni_toolbox::jni(package = "your.package.path", class = "ContainerClass")] fn your_function_name(arg: String) -> Result, String> { Ok(arg.split('/').map(|x| x.to_string()).collect()) } ``` ### conversions every type that must go into/from Java must implement `IntoJava` or `FromJava` (methods will receive a `&mut JNIEnv` and can return errors). most primitives already have them implemented. conversions are automatic and the wrapper function will invoke IntoJava/FromJava for every type, passing an environment reference. ```rust impl<'j> IntoJava for MyClass { type T = jni::sys::jobject; fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result { let hello = env.new_string("world")?; // TODO!! } } ``` ### pointers to return pointer type values, add the `ptr` attribute note that, while possible to pass raw pointers to the JVM, it is not safe by default and must be done with extreme care. ### exceptions Errors are thrown automatically when a `Result` is an error. For your errors to work, you must implement the `JniToolboxError` trait for your errors, (which just returns the path to your Java error class) and then make a Java error wrapper which can be constructed with a single string argument. functions returning `Result`s will automatically have their return value unwrapped and, if is an err, throw an exception and return early. ```rust impl JniToolboxError for MyError { fn jclass(&self) -> String { "my/package/some/MyError".to_string() } } ``` ```java package my.package.some; public class MyError { public MyError(String x) { // TODO } } ``` to throw simple exceptions, it's possible to use the `exception` attribute. just pass your exception's path (must be constructable with a single string argument!) ### examples the following function: ```rust #[jni(package = "mp.code", class = "Client", ptr)] fn connect(config: Config) -> Result { tokio().block_on(Client::connect(config)) } ``` gets turned into these two functions:
show macro expansion ```rust fn connect(config: Config) -> Result { tokio().block_on(Client::connect(config)) } #[no_mangle] #[allow(unused_mut)] pub extern "system" fn Java_mp_code_Client_connect<'local>( mut env: jni::JNIEnv<'local>, _class: jni::objects::JClass<'local>, mut config: >::T, ) -> >::T { use jni_toolbox::{FromJava, IntoJava, JniToolboxError}; let mut env_copy = unsafe { env.unsafe_clone() }; let config_new = match jni_toolbox::from_java_static::(&mut env, config) { Ok(x) => x, Err(e) => { let _ = env.throw_new( "java/lang/RuntimeException", $crate::__export::must_use({ let res = $crate::fmt::format($crate::__export::format_args!("{e:?}")); res }), ); return std::ptr::null_mut(); } }; match connect(config_new) { Err(e) => match env_copy.find_class(e.jclass()) { Err(e) => { $crate::panicking::panic_fmt($crate::const_format_args!( "error throwing Java exception -- failed resolving error class: {e}" )); } Ok(class) => match env_copy.new_string($crate::__export::must_use({ let res = $crate::fmt::format($crate::__export::format_args!("{e:?}")); res })) { Err(e) => { $crate::panicking::panic_fmt($crate::const_format_args!( "error throwing Java exception -- failed creating error string: {e}" )); } Ok(msg) => match env_copy.new_object( class, "(Ljava/lang/String;)V", &[jni::objects::JValueGen::Object(&msg)], ) { Err(e) => { $crate::panicking::panic_fmt($crate::const_format_args!( "error throwing Java exception -- failed creating object: {e}" )); } Ok(obj) => match env_copy.throw(jni::objects::JThrowable::from(obj)) { Err(e) => { $crate::panicking::panic_fmt($crate::const_format_args!( "error throwing Java exception -- failed throwing: {e}" )); } Ok(_) => return std::ptr::null_mut(), }, }, }, }, Ok(ret) => match ret.into_java(&mut env_copy) { Ok(fin) => return fin, Err(e) => { let _ = env_copy.throw_new( "java/lang/RuntimeException", $crate::__export::must_use({ let res = $crate::fmt::format($crate::__export::format_args!("{e:?}")); res }), ); return std::ptr::null_mut(); } }, } } ```
## status this crate is rather early and intended mostly to maintain [`codemp`](https://github.com/hexedtech/codemp) java bindings, however it's also quite small and only runs at comptime, so should be rather safe to use