From 50be86d2a4cf99fa6e4a739fdb8c785dd3a48caa Mon Sep 17 00:00:00 2001 From: alemi Date: Mon, 23 Sep 2024 14:45:26 +0200 Subject: [PATCH] chore: split down main lib file with traits --- src/from_java.rs | 84 ++++++++++++++++ src/into_java.rs | 105 ++++++++++++++++++++ src/lib.rs | 254 ++--------------------------------------------- src/raw_java.rs | 63 ++++++++++++ 4 files changed, 260 insertions(+), 246 deletions(-) create mode 100644 src/from_java.rs create mode 100644 src/into_java.rs create mode 100644 src/raw_java.rs diff --git a/src/from_java.rs b/src/from_java.rs new file mode 100644 index 0000000..e6af7fa --- /dev/null +++ b/src/from_java.rs @@ -0,0 +1,84 @@ +use jni::objects::{JObject, JString}; + + +/// 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::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; + /// Attempts to convert this Java object into its Rust counterpart. + fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::T) -> Result; +} + +macro_rules! auto_from_java { + ($t: ty, $j: ty) => { + impl<'j> FromJava<'j> for $t { + type T = $j; + + #[inline] + fn from_java(_: &mut jni::JNIEnv, value: Self::T) -> Result { + Ok(value) + } + } + }; +} + +auto_from_java!(i64, jni::sys::jlong); +auto_from_java!(i32, jni::sys::jint); +auto_from_java!(i16, jni::sys::jshort); +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>); + +impl<'j> FromJava<'j> for bool { + type T = jni::sys::jboolean; + + #[inline] + fn from_java(_: &mut jni::JNIEnv, value: Self::T) -> Result { + Ok(value == 1) + } +} + +impl<'j> FromJava<'j> for String { + type T = JString<'j>; + + fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::T) -> Result { + 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>>> FromJava<'j> for Option { + type T = T::T; + + fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::T) -> Result { + if value.as_ref().is_null() { return Ok(None) }; + Ok(Some(T::from_java(env, value)?)) + } +} + +#[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 { + let lsb = u64::from_ne_bytes( + env.call_method(&uuid, "getLeastSignificantBits", "()J", &[])? + .j()? + .to_ne_bytes() + ); + + let msb = u64::from_ne_bytes( + env.call_method(&uuid, "getMostSignificantBits", "()J", &[])? + .j()? + .to_ne_bytes() + ); + + Ok(uuid::Uuid::from_u64_pair(msb, lsb)) + } +} diff --git a/src/into_java.rs b/src/into_java.rs new file mode 100644 index 0000000..abc5721 --- /dev/null +++ b/src/into_java.rs @@ -0,0 +1,105 @@ +use jni::objects::{JObject, JObjectArray, JString}; + +use crate::raw_java::FromJavaRaw; + + +/// Specifies how a Rust type should be converted into a Java primitive. +pub trait IntoJavaPrimitive<'j> { + /// The JNI type representing the output. + type T; + /// Attempts to convert this Rust object into a Java primitive. + fn into_java(self, _: &mut jni::JNIEnv<'j>) -> Result; +} + +macro_rules! auto_into_java { + ($t: ty, $j: ty) => { + impl<'j> IntoJavaPrimitive<'j> for $t { + type T = $j; + + fn into_java(self, _: &mut jni::JNIEnv<'j>) -> Result { + Ok(self) + } + } + }; +} + +// TODO: primitive arrays! + +auto_into_java!(i64, jni::sys::jlong); +auto_into_java!(i32, jni::sys::jint); +auto_into_java!(i16, jni::sys::jshort); +auto_into_java!(i8, jni::sys::jbyte); +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; + + #[inline] + fn into_java(self, _: &mut jni::JNIEnv) -> Result { + Ok(if self { 1 } else { 0 }) + } +} + +/// Specifies how a Rust type should be converted into a Java object. +pub trait IntoJavaObject<'j> { + type T: std::convert::AsRef>; + /// 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; +} + +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 { + env.new_string(self) + } +} + +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.as_str().into_java(env) + } +} + +impl<'j, E: IntoJavaObject<'j>> IntoJavaObject<'j> for Vec { + type T = JObjectArray<'j>; + const CLASS: &'static str = E::CLASS; + fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result { + let mut array = env.new_object_array(self.len() as i32, E::CLASS, JObject::null())?; + for (n, el) in self.into_iter().enumerate() { + let el = el.into_java(env)?; + env.set_object_array_element(&mut array, n as i32, &el)?; + } + Ok(array) + } +} + +impl<'j, E: std::convert::AsRef> + FromJavaRaw, T: IntoJavaObject<'j, T = E>> IntoJavaObject<'j> for Option { + type T = E; + const CLASS: &'static str = T::CLASS; + fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result { + 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 + } + } +} + +#[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 { + let class = env.find_class(Self::CLASS)?; + let (msb, lsb) = self.as_u64_pair(); + let msb = i64::from_ne_bytes(msb.to_ne_bytes()); + let lsb = i64::from_ne_bytes(lsb.to_ne_bytes()); + env.new_object(&class, "(JJ)V", &[jni::objects::JValueGen::Long(msb), jni::objects::JValueGen::Long(lsb)]) + } +} diff --git a/src/lib.rs b/src/lib.rs index 56284c3..065043e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,12 @@ +pub mod into_java; +pub mod from_java; +pub mod raw_java; + pub use jni_toolbox_macro::jni; -use jni::objects::{JObject, JObjectArray, JString}; +pub use into_java::{IntoJavaObject, IntoJavaPrimitive}; +pub use from_java::{FromJava, from_java_static}; +pub use raw_java::IntoJavaRaw; + /// An error that is meant to be used with jni-toolbox. pub trait JniToolboxError: std::error::Error { @@ -18,248 +25,3 @@ impl JniToolboxError for jni::errors::JniError { "java/lang/RuntimeException".to_string() } } - -/// 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::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; - /// Attempts to convert this Java object into its Rust counterpart. - fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::T) -> Result; -} - -macro_rules! auto_from_java { - ($t: ty, $j: ty) => { - impl<'j> FromJava<'j> for $t { - type T = $j; - - #[inline] - fn from_java(_: &mut jni::JNIEnv, value: Self::T) -> Result { - Ok(value) - } - } - }; -} - -auto_from_java!(i64, jni::sys::jlong); -auto_from_java!(i32, jni::sys::jint); -auto_from_java!(i16, jni::sys::jshort); -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>); - -impl<'j> FromJava<'j> for bool { - type T = jni::sys::jboolean; - - #[inline] - fn from_java(_: &mut jni::JNIEnv, value: Self::T) -> Result { - Ok(value == 1) - } -} - -impl<'j> FromJava<'j> for String { - type T = JString<'j>; - - fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::T) -> Result { - 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>>> FromJava<'j> for Option { - type T = T::T; - - fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::T) -> Result { - if value.as_ref().is_null() { return Ok(None) }; - Ok(Some(T::from_java(env, value)?)) - } -} - -#[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 { - let lsb = u64::from_ne_bytes( - env.call_method(&uuid, "getLeastSignificantBits", "()J", &[])? - .j()? - .to_ne_bytes() - ); - - let msb = u64::from_ne_bytes( - env.call_method(&uuid, "getMostSignificantBits", "()J", &[])? - .j()? - .to_ne_bytes() - ); - - Ok(uuid::Uuid::from_u64_pair(msb, lsb)) - } -} - -/// Specifies how a Rust type should be converted into a Java primitive. -pub trait IntoJavaPrimitive<'j> { - /// The JNI type representing the output. - type T; - /// Attempts to convert this Rust object into a Java primitive. - fn into_java(self, _: &mut jni::JNIEnv<'j>) -> Result; -} - -macro_rules! auto_into_java { - ($t: ty, $j: ty) => { - impl<'j> IntoJavaPrimitive<'j> for $t { - type T = $j; - - fn into_java(self, _: &mut jni::JNIEnv<'j>) -> Result { - Ok(self) - } - } - }; -} - -// TODO: primitive arrays! - -auto_into_java!(i64, jni::sys::jlong); -auto_into_java!(i32, jni::sys::jint); -auto_into_java!(i16, jni::sys::jshort); -auto_into_java!(i8, jni::sys::jbyte); -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; - - #[inline] - fn into_java(self, _: &mut jni::JNIEnv) -> Result { - Ok(if self { 1 } else { 0 }) - } -} - -/// Specifies how a Rust type should be converted into a Java object. -pub trait IntoJavaObject<'j> { - type T: std::convert::AsRef>; - /// 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; -} - -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 { - env.new_string(self) - } -} - -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.as_str().into_java(env) - } -} - -impl<'j, E: IntoJavaObject<'j>> IntoJavaObject<'j> for Vec { - type T = JObjectArray<'j>; - const CLASS: &'static str = E::CLASS; - fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result { - let mut array = env.new_object_array(self.len() as i32, E::CLASS, JObject::null())?; - for (n, el) in self.into_iter().enumerate() { - let el = el.into_java(env)?; - env.set_object_array_element(&mut array, n as i32, &el)?; - } - Ok(array) - } -} - -impl<'j, E: std::convert::AsRef> + FromJavaRaw, T: IntoJavaObject<'j, T = E>> IntoJavaObject<'j> for Option { - type T = E; - const CLASS: &'static str = T::CLASS; - fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result { - 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 - } - } -} - -#[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 { - let class = env.find_class(Self::CLASS)?; - let (msb, lsb) = self.as_u64_pair(); - let msb = i64::from_ne_bytes(msb.to_ne_bytes()); - let lsb = i64::from_ne_bytes(lsb.to_ne_bytes()); - env.new_object(&class, "(JJ)V", &[jni::objects::JValueGen::Long(msb), jni::objects::JValueGen::Long(lsb)]) - } -} - -/// Needed internally to perform some operations. -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!(jni::sys::jboolean); -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>); diff --git a/src/raw_java.rs b/src/raw_java.rs new file mode 100644 index 0000000..f0d13e3 --- /dev/null +++ b/src/raw_java.rs @@ -0,0 +1,63 @@ +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>);