mirror of
https://github.com/hexedtech/jni-toolbox.git
synced 2024-12-23 05:44:52 +01:00
feat: simplify IntoJava family
basically it's all IntoJava, but there's another trait layer, IntoJavaObject, which downstream library users should implement. Every IntoJavaObject automatically implements IntoJava too
This commit is contained in:
parent
50be86d2a4
commit
5e69eca9d6
8 changed files with 187 additions and 210 deletions
|
@ -1,5 +1,5 @@
|
|||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::TokenStreamExt;
|
||||
use quote::{ToTokens, TokenStreamExt};
|
||||
use syn::Ident;
|
||||
|
||||
pub(crate) struct ArgumentOptions {
|
||||
|
@ -109,7 +109,7 @@ impl ArgumentOptions {
|
|||
},
|
||||
};
|
||||
});
|
||||
incoming.append_all(quote::quote!( mut #pat: <#ty as jni_toolbox::FromJava<'local>>::T,));
|
||||
incoming.append_all(quote::quote!( #pat: <#ty as jni_toolbox::FromJava<'local>>::From,));
|
||||
forwarding.append_all(quote::quote!( #new_pat,));
|
||||
}
|
||||
|
||||
|
@ -121,3 +121,25 @@ struct SingleArgument {
|
|||
pat: syn::Ident,
|
||||
ty: Box<syn::Type>,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn bare_type(t: syn::Type) -> TokenStream {
|
||||
match t {
|
||||
syn::Type::Array(x) => bare_type(*x.elem),
|
||||
syn::Type::BareFn(f) => f.to_token_stream(),
|
||||
syn::Type::Group(x) => bare_type(*x.elem),
|
||||
syn::Type::ImplTrait(t) => t.to_token_stream(),
|
||||
syn::Type::Infer(x) => x.to_token_stream(),
|
||||
syn::Type::Macro(x) => x.to_token_stream(),
|
||||
syn::Type::Never(x) => x.to_token_stream(),
|
||||
syn::Type::Paren(p) => bare_type(*p.elem),
|
||||
syn::Type::Path(p) => p.to_token_stream(),
|
||||
syn::Type::Ptr(x) => bare_type(*x.elem),
|
||||
syn::Type::Reference(r) => bare_type(*r.elem),
|
||||
syn::Type::Slice(s) => bare_type(*s.elem),
|
||||
syn::Type::TraitObject(t) => t.to_token_stream(),
|
||||
syn::Type::Tuple(x) => x.to_token_stream(),
|
||||
syn::Type::Verbatim(x) => x.to_token_stream(),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ pub(crate) struct AttrsOptions {
|
|||
pub(crate) package: String,
|
||||
pub(crate) class: String,
|
||||
pub(crate) exception: Option<String>,
|
||||
pub(crate) return_pointer: bool,
|
||||
}
|
||||
|
||||
impl AttrsOptions {
|
||||
|
@ -14,7 +13,6 @@ impl AttrsOptions {
|
|||
let mut package = None;
|
||||
let mut class = None;
|
||||
let mut exception = None;
|
||||
let mut return_pointer = false;
|
||||
|
||||
for attr in attrs {
|
||||
match what_next {
|
||||
|
@ -24,7 +22,7 @@ impl AttrsOptions {
|
|||
"package" => what_next = WhatNext::Package,
|
||||
"class" => what_next = WhatNext::Class,
|
||||
"exception" => what_next = WhatNext::Exception,
|
||||
"ptr" => return_pointer = true,
|
||||
"ptr" => {}, // accepted for backwards compatibility
|
||||
_ => return Err(syn::Error::new(Span::call_site(), "unexpected attribute on macro: {attr}")),
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +51,7 @@ impl AttrsOptions {
|
|||
let Some(package) = package else { return Err(syn::Error::new(Span::call_site(), "missing required attribute 'package'")) };
|
||||
let Some(class) = class else { return Err(syn::Error::new(Span::call_site(), "missing required attribute 'class'")) };
|
||||
|
||||
Ok(Self { package, class, exception, return_pointer })
|
||||
Ok(Self { package, class, exception })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,13 +6,16 @@ use syn::{ReturnType, Type};
|
|||
pub(crate) struct ReturnOptions {
|
||||
pub(crate) ty: Option<Box<Type>>,
|
||||
pub(crate) result: bool,
|
||||
pub(crate) pointer: bool,
|
||||
pub(crate) void: bool,
|
||||
}
|
||||
|
||||
const PRIMITIVE_TYPES: [&str; 7] = ["i8", "i16", "i32", "i64", "f32", "f64", "bool"];
|
||||
|
||||
impl ReturnOptions {
|
||||
pub(crate) fn parse_signature(ret: &ReturnType) -> Result<Self, syn::Error> {
|
||||
match ret {
|
||||
syn::ReturnType::Default => Ok(Self { ty: None, result: false, void: true }),
|
||||
syn::ReturnType::Default => Ok(Self { ty: None, result: false, void: true, pointer: false }),
|
||||
syn::ReturnType::Type(_tok, ty) => match *ty.clone() {
|
||||
syn::Type::Path(path) => {
|
||||
let Some(last) = path.path.segments.last() else {
|
||||
|
@ -26,14 +29,19 @@ impl ReturnOptions {
|
|||
syn::PathArguments::AngleBracketed(ref generics) => for generic in generics.args.iter() {
|
||||
match generic {
|
||||
syn::GenericArgument::Lifetime(_) => continue,
|
||||
syn::GenericArgument::Type(ty) => return Ok(Self { ty: Some(Box::new(ty.clone())), result: true, void: is_void(ty) }),
|
||||
_ => return Err(syn::Error::new(Span::call_site(), "unexpected type in Result")),
|
||||
syn::GenericArgument::Type(ty) => {
|
||||
// TODO checking by making ty a token stream and then string equals is not exactly great!
|
||||
let pointer = !PRIMITIVE_TYPES.iter().any(|t| ty.to_token_stream().to_string() == *t);
|
||||
return Ok(Self { ty: Some(Box::new(ty.clone())), result: true, void: is_void(ty), pointer });
|
||||
},
|
||||
_ => return Err(syn::Error::new(Span::call_site(), "unexpected type in Result"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self { ty: Some(Box::new(Type::Path(path.clone()))), result: false, void: false })
|
||||
let pointer = !PRIMITIVE_TYPES.iter().any(|t| last.ident == t);
|
||||
Ok(Self { ty: Some(Box::new(Type::Path(path.clone()))), result: false, void: false, pointer })
|
||||
},
|
||||
_ => Err(syn::Error::new(Span::call_site(), "unsupported return type")),
|
||||
},
|
||||
|
@ -43,13 +51,7 @@ impl ReturnOptions {
|
|||
pub(crate) fn tokens(&self) -> TokenStream {
|
||||
match &self.ty { // TODO why do we need to invoke syn::Token! macro ???
|
||||
None => ReturnType::Default.to_token_stream(),
|
||||
Some(t) => {
|
||||
if t.to_token_stream().to_string() == "bool" {// TODO: there DEFINITELY is a better way
|
||||
quote::quote!( -> jni::sys::jboolean )
|
||||
} else {
|
||||
quote::quote!( -> <#t as jni_toolbox::IntoJavaRaw>::T )
|
||||
}
|
||||
}
|
||||
Some(t) => quote::quote!( -> <#t as jni_toolbox::IntoJava<'local>>::Ret )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ pub(crate) fn generate_jni_wrapper(attrs: TokenStream, input: TokenStream) -> Re
|
|||
let ret = ReturnOptions::parse_signature(&fn_item.sig.output)?;
|
||||
let return_expr = if ret.void {
|
||||
quote::quote!( () )
|
||||
} else if attrs.return_pointer {
|
||||
} else if ret.pointer {
|
||||
quote::quote!( std::ptr::null_mut() )
|
||||
} else {
|
||||
quote::quote!( 0 )
|
||||
|
@ -25,11 +25,6 @@ pub(crate) fn generate_jni_wrapper(attrs: TokenStream, input: TokenStream) -> Re
|
|||
let args = ArgumentOptions::parse_args(&fn_item, return_expr.clone())?;
|
||||
|
||||
let return_type = ret.tokens();
|
||||
let return_type_import = if attrs.return_pointer {
|
||||
syn::Ident::new("IntoJavaObject", Span::call_site())
|
||||
} else {
|
||||
syn::Ident::new("IntoJavaPrimitive", Span::call_site())
|
||||
};
|
||||
|
||||
let name = fn_item.sig.ident.to_string();
|
||||
let name_jni = name.replace("_", "_1");
|
||||
|
@ -38,100 +33,88 @@ pub(crate) fn generate_jni_wrapper(attrs: TokenStream, input: TokenStream) -> Re
|
|||
|
||||
let incoming = args.incoming;
|
||||
// V----------------------------------V
|
||||
let header = if attrs.return_pointer {
|
||||
quote::quote! {
|
||||
#[no_mangle]
|
||||
#[allow(unused_mut)]
|
||||
pub extern "system" fn #fn_name<'local>(#incoming) -> jni::sys::jobject
|
||||
}
|
||||
} else {
|
||||
quote::quote! {
|
||||
#[no_mangle]
|
||||
#[allow(unused_mut)]
|
||||
pub extern "system" fn #fn_name<'local>(#incoming) #return_type
|
||||
}
|
||||
let header = quote::quote! {
|
||||
#[no_mangle]
|
||||
#[allow(unused_unit)]
|
||||
pub extern "system" fn #fn_name<'local>(#incoming) #return_type
|
||||
};
|
||||
|
||||
|
||||
// ^----------------------------------^
|
||||
let transforming = args.transforming;
|
||||
let transformations = quote::quote! {
|
||||
use jni_toolbox::{JniToolboxError, FromJava, IntoJava};
|
||||
#transforming
|
||||
};
|
||||
|
||||
|
||||
let env_ident = args.env;
|
||||
let forwarding = args.forwarding;
|
||||
let transforming = args.transforming;
|
||||
let body = if ret.result { // wrap errors
|
||||
let invocation = quote::quote! {
|
||||
let mut env_copy = unsafe { #env_ident.unsafe_clone() };
|
||||
let result = #fn_name_inner(#forwarding);
|
||||
};
|
||||
|
||||
|
||||
let error_handling = if ret.result {
|
||||
if let Some(exception) = attrs.exception {
|
||||
// V----------------------------------V
|
||||
quote::quote! {
|
||||
{
|
||||
use jni_toolbox::#return_type_import;
|
||||
use jni_toolbox::{JniToolboxError, FromJava};
|
||||
#transforming
|
||||
match #fn_name_inner(#forwarding) {
|
||||
Ok(ret) => ret,
|
||||
Err(e) => match #env_ident.throw_new(#exception, format!("{e:?}")) {
|
||||
Ok(_) => return #return_expr,
|
||||
Err(e) => panic!("error throwing java exception: {e}"),
|
||||
}
|
||||
let ret = match result {
|
||||
Ok(x) => x,
|
||||
Err(e) => match env_copy.throw_new(#exception, format!("{e:?}")) {
|
||||
Ok(_) => return #return_expr,
|
||||
Err(e) => panic!("error throwing java exception: {e}"),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
// ^----------------------------------^
|
||||
} else {
|
||||
// V----------------------------------V
|
||||
quote::quote! {
|
||||
{
|
||||
use jni_toolbox::#return_type_import;
|
||||
use jni_toolbox::{JniToolboxError, FromJava, IntoJavaRaw};
|
||||
// NOTE: this should be SAFE! the cloned env reference lives less than the actual one, we just lack a
|
||||
// way to get it back from the called function and thus resort to unsafe cloning
|
||||
let mut env_copy = unsafe { #env_ident.unsafe_clone() };
|
||||
#transforming
|
||||
match #fn_name_inner(#forwarding) {
|
||||
Err(e) => match env_copy.find_class(e.jclass()) {
|
||||
Err(e) => panic!("error throwing Java exception -- failed resolving error class: {e}"),
|
||||
Ok(class) => match env_copy.new_string(format!("{e:?}")) {
|
||||
Err(e) => panic!("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) => panic!("error throwing Java exception -- failed creating object: {e}"),
|
||||
Ok(obj) => match env_copy.throw(jni::objects::JThrowable::from(obj)) {
|
||||
Err(e) => panic!("error throwing Java exception -- failed throwing: {e}"),
|
||||
Ok(_) => return #return_expr,
|
||||
},
|
||||
let ret = match result {
|
||||
Ok(x) => x,
|
||||
Err(e) => match env_copy.find_class(e.jclass()) {
|
||||
Err(e) => panic!("error throwing Java exception -- failed resolving error class: {e}"),
|
||||
Ok(class) => match env_copy.new_string(format!("{e:?}")) {
|
||||
Err(e) => panic!("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) => panic!("error throwing Java exception -- failed creating object: {e}"),
|
||||
Ok(obj) => match env_copy.throw(jni::objects::JThrowable::from(obj)) {
|
||||
Err(e) => panic!("error throwing Java exception -- failed throwing: {e}"),
|
||||
Ok(_) => return #return_expr,
|
||||
},
|
||||
},
|
||||
}
|
||||
Ok(ret) => match ret.into_java(&mut env_copy) {
|
||||
Ok(fin) => return fin.into_java_raw(),
|
||||
Err(e) => {
|
||||
// TODO should we panic instead?
|
||||
let _ = env_copy.throw_new("java/lang/RuntimeException", format!("{e:?}"));
|
||||
return #return_expr;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// V----------------------------------V
|
||||
quote::quote! {
|
||||
{
|
||||
use jni_toolbox::#return_type_import;
|
||||
use jni_toolbox::{JniToolboxError, FromJava, IntoJavaRaw};
|
||||
#transforming
|
||||
match #fn_name_inner(#forwarding).into_java(&mut #env_ident) {
|
||||
Ok(res) => return res.into_java_raw(),
|
||||
Err(e) => {
|
||||
// TODO should we panic instead?
|
||||
let _ = #env_ident.throw_new("java/lang/RuntimeException", format!("{e:?}"));
|
||||
return #return_expr;
|
||||
},
|
||||
}
|
||||
quote::quote!( let ret = result; )
|
||||
};
|
||||
|
||||
|
||||
let reverse_transformations = quote::quote! {
|
||||
match ret.into_java(&mut env_copy) {
|
||||
Ok(fin) => fin,
|
||||
Err(e) => {
|
||||
// TODO should we panic instead?
|
||||
let _ = env_copy.throw_new("java/lang/RuntimeException", format!("{e:?}"));
|
||||
#return_expr
|
||||
}
|
||||
}
|
||||
// ^----------------------------------^
|
||||
};
|
||||
|
||||
let body = quote::quote! {
|
||||
{
|
||||
#transformations
|
||||
|
||||
#invocation
|
||||
|
||||
#error_handling
|
||||
|
||||
#reverse_transformations
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
out.append_all(input);
|
||||
out.append_all(header);
|
||||
out.append_all(body);
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
use jni::objects::{JObject, JString};
|
||||
use jni::objects::{JObject, JObjectArray, JPrimitiveArray, JString, TypeArray};
|
||||
|
||||
|
||||
/// 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::From) -> Result<T, jni::errors::Error> {
|
||||
T::from_java(env, val)
|
||||
}
|
||||
|
||||
/// Specifies how a Java type should be converted before being fed to Rust.
|
||||
pub trait FromJava<'j> : Sized {
|
||||
/// The JNI type representing the input.
|
||||
type T : Sized;
|
||||
type From : 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::From) -> Result<Self, jni::errors::Error>;
|
||||
}
|
||||
|
||||
macro_rules! auto_from_java {
|
||||
($t: ty, $j: ty) => {
|
||||
impl<'j> FromJava<'j> for $t {
|
||||
type T = $j;
|
||||
type From = $j;
|
||||
|
||||
#[inline]
|
||||
fn from_java(_: &mut jni::JNIEnv, value: Self::T) -> Result<Self, jni::errors::Error> {
|
||||
fn from_java(_: &mut jni::JNIEnv, value: Self::From) -> Result<Self, jni::errors::Error> {
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
@ -35,38 +35,75 @@ auto_from_java!(i8, jni::sys::jbyte);
|
|||
auto_from_java!(f32, jni::sys::jfloat);
|
||||
auto_from_java!(f64, jni::sys::jdouble);
|
||||
auto_from_java!(JObject<'j>, JObject<'j>);
|
||||
auto_from_java!(JString<'j>, JString<'j>);
|
||||
auto_from_java!(JObjectArray<'j>, JObjectArray<'j>);
|
||||
|
||||
impl<'j> FromJava<'j> for bool {
|
||||
type T = jni::sys::jboolean;
|
||||
impl<'j, T: TypeArray> FromJava<'j> for JPrimitiveArray<'j, T> {
|
||||
type From = JPrimitiveArray<'j, T>;
|
||||
|
||||
#[inline]
|
||||
fn from_java(_: &mut jni::JNIEnv, value: Self::T) -> Result<Self, jni::errors::Error> {
|
||||
fn from_java(_: &mut jni::JNIEnv, value: Self::From) -> Result<Self, jni::errors::Error> {
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'j> FromJava<'j> for char {
|
||||
type From = jni::sys::jchar;
|
||||
|
||||
#[inline]
|
||||
fn from_java(_: &mut jni::JNIEnv, value: Self::From) -> Result<Self, jni::errors::Error> {
|
||||
char::from_u32(value.into()).ok_or_else(|| jni::errors::Error::WrongJValueType("char", "invalid u16"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'j> FromJava<'j> for bool {
|
||||
type From = jni::sys::jboolean;
|
||||
|
||||
#[inline]
|
||||
fn from_java(_: &mut jni::JNIEnv, value: Self::From) -> Result<Self, jni::errors::Error> {
|
||||
Ok(value == 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'j> FromJava<'j> for String {
|
||||
type T = JString<'j>;
|
||||
type From = JString<'j>;
|
||||
|
||||
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::From) -> Result<Self, jni::errors::Error> {
|
||||
if value.is_null() { return Err(jni::errors::Error::NullPtr("string can't be null")) };
|
||||
Ok(env.get_string(&value)?.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'j, T: FromJava<'j, T: std::convert::AsRef<JObject<'j>>>> FromJava<'j> for Option<T> {
|
||||
type T = T::T;
|
||||
impl<'j, T> FromJava<'j> for Option<T>
|
||||
where
|
||||
T: FromJava<'j, From: AsRef<JObject<'j>>>,
|
||||
{
|
||||
type From = T::From;
|
||||
|
||||
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::From) -> Result<Self, jni::errors::Error> {
|
||||
if value.as_ref().is_null() { return Ok(None) };
|
||||
Ok(Some(T::from_java(env, value)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'j, T: FromJava<'j, From = JObject<'j>>> FromJava<'j> for Vec<T> {
|
||||
type From = JObjectArray<'j>;
|
||||
|
||||
fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::From) -> Result<Self, jni::errors::Error> {
|
||||
let len = env.get_array_length(&value)?;
|
||||
let mut out = Vec::new();
|
||||
for i in 0..len {
|
||||
let el = env.get_object_array_element(&value, i)?;
|
||||
out.push(T::from_java(env, el)?);
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "uuid")]
|
||||
impl<'j> FromJava<'j> for uuid::Uuid {
|
||||
type T = JObject<'j>;
|
||||
fn from_java(env: &mut jni::JNIEnv<'j>, uuid: Self::T) -> Result<Self, jni::errors::Error> {
|
||||
type From = JObject<'j>;
|
||||
fn from_java(env: &mut jni::JNIEnv<'j>, uuid: Self::From) -> Result<Self, jni::errors::Error> {
|
||||
let lsb = u64::from_ne_bytes(
|
||||
env.call_method(&uuid, "getLeastSignificantBits", "()J", &[])?
|
||||
.j()?
|
||||
|
|
|
@ -1,22 +1,20 @@
|
|||
use jni::objects::{JObject, JObjectArray, JString};
|
||||
|
||||
use crate::raw_java::FromJavaRaw;
|
||||
use jni::objects::JObject;
|
||||
|
||||
|
||||
/// Specifies how a Rust type should be converted into a Java primitive.
|
||||
pub trait IntoJavaPrimitive<'j> {
|
||||
pub trait IntoJava<'j> {
|
||||
/// The JNI type representing the output.
|
||||
type T;
|
||||
type Ret;
|
||||
/// 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::Ret, jni::errors::Error>;
|
||||
}
|
||||
|
||||
macro_rules! auto_into_java {
|
||||
($t: ty, $j: ty) => {
|
||||
impl<'j> IntoJavaPrimitive<'j> for $t {
|
||||
type T = $j;
|
||||
impl<'j> IntoJava<'j> for $t {
|
||||
type Ret = $j;
|
||||
|
||||
fn into_java(self, _: &mut jni::JNIEnv<'j>) -> Result<Self::T, jni::errors::Error> {
|
||||
fn into_java(self, _: &mut jni::JNIEnv<'j>) -> Result<Self::Ret, jni::errors::Error> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
@ -33,69 +31,71 @@ auto_into_java!(f32, jni::sys::jfloat);
|
|||
auto_into_java!(f64, jni::sys::jdouble);
|
||||
auto_into_java!((), ());
|
||||
|
||||
impl<'j> IntoJavaPrimitive<'j> for bool {
|
||||
type T = jni::sys::jboolean;
|
||||
impl<'j> IntoJava<'j> for bool {
|
||||
type Ret = jni::sys::jboolean;
|
||||
|
||||
#[inline]
|
||||
fn into_java(self, _: &mut jni::JNIEnv) -> Result<Self::T, jni::errors::Error> {
|
||||
fn into_java(self, _: &mut jni::JNIEnv) -> Result<Self::Ret, jni::errors::Error> {
|
||||
Ok(if self { 1 } else { 0 })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'j, X: IntoJavaObject<'j>> IntoJava<'j> for X {
|
||||
type Ret = jni::sys::jobject;
|
||||
|
||||
fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result<Self::Ret, jni::errors::Error> {
|
||||
Ok(self.into_java_object(env)?.as_raw())
|
||||
}
|
||||
}
|
||||
|
||||
/// Specifies how a Rust type should be converted into a Java object.
|
||||
pub trait IntoJavaObject<'j> {
|
||||
type T: std::convert::AsRef<JObject<'j>>;
|
||||
/// The Java class associated with this type.
|
||||
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_object(self, env: &mut jni::JNIEnv<'j>) -> Result<JObject<'j>, jni::errors::Error>;
|
||||
}
|
||||
|
||||
impl<'j> IntoJavaObject<'j> for &str {
|
||||
type T = JString<'j>;
|
||||
const CLASS: &'static str = "java/lang/String";
|
||||
fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result<Self::T, jni::errors::Error> {
|
||||
env.new_string(self)
|
||||
fn into_java_object(self, env: &mut jni::JNIEnv<'j>) -> Result<JObject<'j>, jni::errors::Error> {
|
||||
Ok(env.new_string(self)?.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'j> IntoJavaObject<'j> for String {
|
||||
type T = JString<'j>;
|
||||
const CLASS: &'static str = "java/lang/String";
|
||||
fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result<Self::T, jni::errors::Error> {
|
||||
self.as_str().into_java(env)
|
||||
fn into_java_object(self, env: &mut jni::JNIEnv<'j>) -> Result<JObject<'j>, jni::errors::Error> {
|
||||
self.as_str().into_java_object(env)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'j, E: IntoJavaObject<'j>> IntoJavaObject<'j> for Vec<E> {
|
||||
type T = JObjectArray<'j>;
|
||||
const CLASS: &'static str = E::CLASS;
|
||||
fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result<Self::T, jni::errors::Error> {
|
||||
let mut array = env.new_object_array(self.len() as i32, E::CLASS, JObject::null())?;
|
||||
impl<'j, T: IntoJavaObject<'j>> IntoJavaObject<'j> for Vec<T> {
|
||||
const CLASS: &'static str = T::CLASS; // TODO shouldnt it be 'Object[]' ?
|
||||
fn into_java_object(self, env: &mut jni::JNIEnv<'j>) -> Result<JObject<'j>, jni::errors::Error> {
|
||||
let mut array = env.new_object_array(self.len() as i32, T::CLASS, JObject::null())?;
|
||||
for (n, el) in self.into_iter().enumerate() {
|
||||
let el = el.into_java(env)?;
|
||||
let el = el.into_java_object(env)?;
|
||||
env.set_object_array_element(&mut array, n as i32, &el)?;
|
||||
}
|
||||
Ok(array)
|
||||
Ok(array.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'j, E: std::convert::AsRef<JObject<'j>> + FromJavaRaw, T: IntoJavaObject<'j, T = E>> IntoJavaObject<'j> for Option<T> {
|
||||
type T = E;
|
||||
impl<'j, T: IntoJavaObject<'j>> IntoJavaObject<'j> for Option<T> {
|
||||
const CLASS: &'static str = T::CLASS;
|
||||
fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result<Self::T, jni::errors::Error> {
|
||||
fn into_java_object(self, env: &mut jni::JNIEnv<'j>) -> Result<JObject<'j>, jni::errors::Error> {
|
||||
match self {
|
||||
Some(x) => x.into_java(env),
|
||||
None => Ok(unsafe { E::from_java_raw(std::ptr::null_mut()) }) // safe, that's what JObject::null does
|
||||
Some(x) => x.into_java_object(env),
|
||||
None => Ok(JObject::null())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "uuid")]
|
||||
impl<'j> IntoJavaObject<'j> for uuid::Uuid {
|
||||
type T = jni::objects::JObject<'j>;
|
||||
const CLASS: &'static str = "java/util/UUID";
|
||||
fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result<Self::T, jni::errors::Error> {
|
||||
fn into_java_object(self, env: &mut jni::JNIEnv<'j>) -> Result<JObject<'j>, jni::errors::Error> {
|
||||
let class = env.find_class(Self::CLASS)?;
|
||||
let (msb, lsb) = self.as_u64_pair();
|
||||
let msb = i64::from_ne_bytes(msb.to_ne_bytes());
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
pub mod into_java;
|
||||
pub mod from_java;
|
||||
pub mod raw_java;
|
||||
|
||||
pub use jni_toolbox_macro::jni;
|
||||
pub use into_java::{IntoJavaObject, IntoJavaPrimitive};
|
||||
pub use into_java::{IntoJavaObject, IntoJava};
|
||||
pub use from_java::{FromJava, from_java_static};
|
||||
pub use raw_java::IntoJavaRaw;
|
||||
|
||||
|
||||
/// An error that is meant to be used with jni-toolbox.
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
use jni::objects::{JObject, JObjectArray, JString};
|
||||
|
||||
|
||||
/// Needed internally to perform some operations.
|
||||
pub trait FromJavaRaw {
|
||||
unsafe fn from_java_raw(raw: jni::sys::jobject) -> Self;
|
||||
}
|
||||
|
||||
macro_rules! auto_from_raw {
|
||||
($type: ty) => {
|
||||
impl FromJavaRaw for $type {
|
||||
#[inline]
|
||||
unsafe fn from_java_raw(raw: jni::sys::jobject) -> Self {
|
||||
Self::from_raw(raw)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
auto_from_raw!(JObject<'_>);
|
||||
auto_from_raw!(JString<'_>);
|
||||
auto_from_raw!(JObjectArray<'_>);
|
||||
|
||||
/// Intermediate trait used to guess the JNI return type.
|
||||
/// Usually doesn't need to be manually implemented.
|
||||
pub trait IntoJavaRaw {
|
||||
type T;
|
||||
fn into_java_raw(self) -> Self::T;
|
||||
}
|
||||
|
||||
macro_rules! auto_into_raw_primitive {
|
||||
($type: ty) => {
|
||||
impl IntoJavaRaw for $type {
|
||||
type T = $type;
|
||||
fn into_java_raw(self) -> Self::T {
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto_into_raw_primitive!(jni::sys::jlong);
|
||||
auto_into_raw_primitive!(jni::sys::jint);
|
||||
auto_into_raw_primitive!(jni::sys::jshort);
|
||||
auto_into_raw_primitive!(jni::sys::jbyte);
|
||||
auto_into_raw_primitive!(jni::sys::jdouble);
|
||||
auto_into_raw_primitive!(jni::sys::jfloat);
|
||||
auto_into_raw_primitive!(());
|
||||
|
||||
macro_rules! auto_into_raw_object {
|
||||
($lt: lifetime, $type: ty) => {
|
||||
impl<'j> IntoJavaRaw for $type {
|
||||
type T = jni::sys::jobject;
|
||||
fn into_java_raw(self) -> Self::T {
|
||||
self.as_raw()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
auto_into_raw_object!('j, JObject<'j>);
|
||||
auto_into_raw_object!('j, JString<'j>);
|
||||
auto_into_raw_object!('j, JObjectArray<'j>);
|
Loading…
Reference in a new issue