docs: updated README, minimal rustdocs

This commit is contained in:
zaaarf 2024-09-23 00:50:52 +02:00
parent d6c0aa2f9e
commit 6f424dadf7
No known key found for this signature in database
GPG key ID: 102E445F4C3F829B
3 changed files with 40 additions and 32 deletions

View file

@ -3,13 +3,12 @@
[![Crates.io Version](https://img.shields.io/crates/v/jni-toolbox)](https://crates.io/crates/jni-toolbox) [![Crates.io Version](https://img.shields.io/crates/v/jni-toolbox)](https://crates.io/crates/jni-toolbox)
[![docs.rs](https://img.shields.io/docsrs/jni-toolbox)](https://docs.rs/jni-toolbox) [![docs.rs](https://img.shields.io/docsrs/jni-toolbox)](https://docs.rs/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.
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.
it also wraps functions returning `Result<>`, making short-circuiting easy ## Usage
Just specify package and class on your function, and done!
## usage
just specify package and class on your function, and done!
```rust ```rust
#[jni_toolbox::jni(package = "your.package.path", class = "ContainerClass")] #[jni_toolbox::jni(package = "your.package.path", class = "ContainerClass")]
@ -18,14 +17,13 @@ fn your_function_name(arg: String) -> Result<Vec<String>, String> {
} }
``` ```
### conversions ### Conversions
every type that must go into/from Java must implement `IntoJava` or `FromJava` (methods will receive a `&mut JNIEnv` and can return errors). Every type that is meant to be sent to Java must implement `IntoJavaObject` (or, unlikely, `IntoJavaPrimitive`); every type that is meant to be
most primitives already have them implemented. conversions are automatic and the wrapper function will invoke IntoJava/FromJava for every type, received from Java must implement `FromJava`. Most primitives and a few common types should already be implemented.
passing an environment reference.
```rust ```rust
impl<'j> IntoJava for MyClass { impl<'j> IntoJavaObject for MyClass {
type T = jni::sys::jobject; type T = jni::objects::JObject<'j>
fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result<Self::T, jni::errors::Error> { fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result<Self::T, jni::errors::Error> {
let hello = env.new_string("world")?; let hello = env.new_string("world")?;
// TODO!! // TODO!!
@ -33,15 +31,16 @@ impl<'j> IntoJava for MyClass {
} }
``` ```
### pointers ### Pointers
to return pointer type values, add the `ptr` attribute 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. Note that, while it is possible to pass raw pointers to the JVM, it is not safe by default and must be done with extreme care.
### exceptions ### 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, 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. (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.
Functions returning `Result`s will automatically have their return value unwrapped and, if is an err, throw an exception and return early.
```rust ```rust
impl JniToolboxError for MyError { impl JniToolboxError for MyError {
@ -53,19 +52,18 @@ impl JniToolboxError for MyError {
```java ```java
package my.package.some; package my.package.some;
public class MyError { public class MyError extends Throwable {
public MyError(String x) { public MyError(String x) {
// TODO // 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!) To throw simple exceptions, it's possible to use the `exception` attribute. Pass the exception's fully qualified name (must have a constructor
that takes in a single `String` argument).
### Examples
The following function:
### examples
the following function:
```rust ```rust
#[jni(package = "mp.code", class = "Client", ptr)] #[jni(package = "mp.code", class = "Client", ptr)]
fn connect(config: Config) -> Result<Client, ConnectionError> { fn connect(config: Config) -> Result<Client, ConnectionError> {
@ -73,15 +71,11 @@ fn connect(config: Config) -> Result<Client, ConnectionError> {
} }
``` ```
gets turned into these two functions: generates a matching expanded function invoking it:
<details><summary>show macro expansion</summary> <details><summary>Show macro expansion</summary>
```rust ```rust
fn connect(config: Config) -> Result<Client, ConnectionError> {
tokio().block_on(Client::connect(config))
}
#[no_mangle] #[no_mangle]
#[allow(unused_mut)] #[allow(unused_mut)]
pub extern "system" fn Java_mp_code_Client_connect<'local>( pub extern "system" fn Java_mp_code_Client_connect<'local>(
@ -157,9 +151,10 @@ pub extern "system" fn Java_mp_code_Client_connect<'local>(
} }
} }
``` ```
</details> </details>
## status ## 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 This crate is early and intended mostly to maintain [`codemp`](https://github.com/hexedtech/codemp)'s Java bindings, so things not used
there may be missing or slightly broken. However, the crate is also quite small and only runs at compile time, so trying it out in your
own project should not be a problem.

View file

@ -3,7 +3,7 @@ mod wrapper;
mod args; mod args;
mod ret; mod ret;
/// wrap this function in in a JNI exported fn /// Wrap this function in in a JNI exported fn.
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn jni( pub fn jni(
attrs: proc_macro::TokenStream, attrs: proc_macro::TokenStream,

View file

@ -1,7 +1,9 @@
pub use jni_toolbox_macro::jni; pub use jni_toolbox_macro::jni;
use jni::objects::{JObject, JObjectArray, JString}; use jni::objects::{JObject, JObjectArray, JString};
/// An error that is meant to be used with jni-toolbox.
pub trait JniToolboxError: std::error::Error { pub trait JniToolboxError: std::error::Error {
/// The Java class for the matching exception.
fn jclass(&self) -> String; fn jclass(&self) -> String;
} }
@ -17,12 +19,17 @@ impl JniToolboxError for jni::errors::JniError {
} }
} }
/// Used in the generated code to have proper type bindings. You probably didn't want
/// to call this directly.
pub fn from_java_static<'j, T: FromJava<'j>>(env: &mut jni::JNIEnv<'j>, val: T::T) -> Result<T, jni::errors::Error> { pub fn from_java_static<'j, T: FromJava<'j>>(env: &mut jni::JNIEnv<'j>, val: T::T) -> Result<T, jni::errors::Error> {
T::from_java(env, val) T::from_java(env, val)
} }
/// Specifies how a Java type should be converted before being fed to Rust.
pub trait FromJava<'j> : Sized { pub trait FromJava<'j> : Sized {
/// The JNI type representing the input.
type T : Sized; type T : Sized;
/// Attempts to convert this Java object into its Rust counterpart.
fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::T) -> Result<Self, jni::errors::Error>; fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::T) -> Result<Self, jni::errors::Error>;
} }
@ -94,8 +101,11 @@ impl<'j> FromJava<'j> for uuid::Uuid {
} }
} }
/// Specifies how a Rust type should be converted into a Java primitive.
pub trait IntoJavaPrimitive<'j> { pub trait IntoJavaPrimitive<'j> {
/// The JNI type representing the output.
type T; type T;
/// Attempts to convert this Rust object into a Java primitive.
fn into_java(self, _: &mut jni::JNIEnv<'j>) -> Result<Self::T, jni::errors::Error>; fn into_java(self, _: &mut jni::JNIEnv<'j>) -> Result<Self::T, jni::errors::Error>;
} }
@ -130,9 +140,12 @@ impl<'j> IntoJavaPrimitive<'j> for bool {
} }
} }
/// Specifies how a Rust type should be converted into a Java object.
pub trait IntoJavaObject<'j> { pub trait IntoJavaObject<'j> {
type T: std::convert::AsRef<JObject<'j>>; type T: std::convert::AsRef<JObject<'j>>;
/// The Java class associated with this type.
const CLASS: &'static str; const CLASS: &'static str;
/// Attempts to convert this Rust object into a Java object.
fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result<Self::T, jni::errors::Error>; fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result<Self::T, jni::errors::Error>;
} }