use jni::objects::JString;
use jni::JNIEnv;
use crate::type_bindings::android;
#[derive(Debug)]
pub enum StringConversionError {
JniError(jni::errors::Error),
}
impl From<jni::errors::Error> for StringConversionError {
fn from(err: jni::errors::Error) -> Self {
StringConversionError::JniError(err)
}
}
pub trait ToNativeString {
fn to_native(&self, env: &mut JNIEnv) -> Result<android::ffi::String, StringConversionError>;
}
pub trait FromNativeString: Sized {
fn from_native(
native: android::ffi::String,
env: &mut jni::JNIEnv,
) -> Result<Self, StringConversionError>;
}
impl ToNativeString for String {
fn to_native(&self, env: &mut JNIEnv) -> Result<android::ffi::String, StringConversionError> {
let jstr = env.new_string(self)?;
Ok(jstr.into_raw())
}
}
impl ToNativeString for &str {
fn to_native(&self, env: &mut JNIEnv) -> Result<android::ffi::String, StringConversionError> {
let jstr = env.new_string(*self)?;
Ok(jstr.into_raw())
}
}
impl FromNativeString for String {
fn from_native(
jstr: android::ffi::String,
env: &mut JNIEnv,
) -> Result<Self, StringConversionError> {
unsafe {
let jstring = JString::from_raw(jstr);
let java_str = env.get_string(&jstring)?;
Ok(java_str.into())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_to_native() {
let jvm_args = jni::InitArgsBuilder::new()
.version(jni::JNIVersion::V8)
.option("-Xcheck:jni")
.build()
.unwrap();
let jvm = jni::JavaVM::new(jvm_args).unwrap();
let _guard = jvm.attach_current_thread().unwrap();
let mut env = jvm.get_env().unwrap();
let str = "Hello, world!".to_native(&mut env).unwrap();
println!("str_ptr: {:?}", str);
assert_eq!(
std::any::type_name_of_val(&str),
"*mut jni_sys::_jobject"
);
let str = String::from_native(str, &mut env).unwrap();
assert_eq!(str, "Hello, world!");
}
}