use crate::common::{is_array_arg_barefn, is_result_arg_barefn, is_user_data_arg_barefn};
use crate::java::Context;
use crate::{Error, Level};
use jni::signature::{JavaType, Primitive};
use syn::export::ToTokens;
use unwrap::unwrap;
fn primitive_type_to_str(ty: Primitive) -> &'static str {
match ty {
Primitive::Boolean => "boolean",
Primitive::Byte => "byte",
Primitive::Char => "char",
Primitive::Double => "double",
Primitive::Float => "float",
Primitive::Int => "int",
Primitive::Long => "long",
Primitive::Short => "short",
Primitive::Void => "void",
}
}
pub fn java_type_to_str(ty: &JavaType) -> Result<String, Error> {
match *ty {
JavaType::Primitive(primitive) => Ok(primitive_type_to_str(primitive).to_string()),
JavaType::Object(ref obj) => match obj.as_str() {
"java/lang/String" => Ok("String".into()),
_ => Ok(obj.to_string()),
},
JavaType::Array(ref boxed) => Ok(format!("{}[]", java_type_to_str(&*boxed)?)),
JavaType::Method(..) => Err(Error {
level: Level::Error,
span: None,
message: "Java methods are not supported".into(),
}),
}
}
pub fn struct_to_java_classname<S: AsRef<str>>(s: S) -> String {
let mut c = s.as_ref().chars();
match c.next() {
None => String::new(),
Some(f) => f.to_uppercase().collect::<String>() + c.as_str(),
}
}
pub fn callback_name(inputs: &[syn::BareFnArg], context: &Context) -> Result<String, Error> {
let mut components = Vec::new();
let mut inputs = inputs.iter().peekable();
while let Some(arg) = inputs.next() {
if is_user_data_arg_barefn(&arg.clone()) {
continue;
}
if is_result_arg_barefn(&arg) {
components.push(From::from("Result"));
continue;
}
let arg_type = &rust_ty_to_java_class_name(&arg.ty, context)?;
let mut arg_type = struct_to_java_classname(arg_type);
if is_array_arg_barefn(&arg, inputs.peek().cloned()) {
inputs.next();
arg_type.push_str("ArrayLen");
}
components.push(arg_type);
}
if components.is_empty() {
Ok(From::from("CallbackVoid"))
} else {
Ok(format!("Callback{}", components.join("")))
}
}
fn callback_arg_to_java(
fn_ty: &syn::TypeBareFn,
context: &Context,
) -> Result<JavaType, Error> {
match unwrap!(unwrap!(fn_ty.abi.to_owned()).name).value().as_str() {
"C" | "Cdecl" | "Stdcall" | "Fastcall" | "System" => {}
_ => {
return Err(Error {
level: Level::Error,
span: None, message: "callbacks that don't have C ABI are not supported".into(),
});
}
}
if fn_ty.lifetimes.is_some() {
return Err(Error {
level: Level::Error,
span: None, message: "cannot handle lifetimes".into(),
});
}
let mut vec = vec![];
for x in fn_ty.inputs.to_owned() {
vec.push(x);
}
Ok(JavaType::Object(callback_name(vec.as_slice(), context)?))
}
pub fn rust_to_java(ty: &syn::Type, context: &Context) -> Result<JavaType, Error> {
match ty {
syn::Type::BareFn(ref bare_fn) => callback_arg_to_java(bare_fn, context),
_ => anon_rust_to_java(ty, context, true),
}
}
fn rust_ty_to_java_class_name(ty: &syn::Type, context: &Context) -> Result<String, Error> {
match ty {
syn::Type::Path(ref path) => {
let primitive_type: String = path.path.segments[0].ident.to_string();
if primitive_type.as_str() == "usize" || primitive_type == "isize" {
Ok(From::from("size"))
} else {
java_type_to_str(&path_to_java(&path.path, context, false)?)
}
}
_ => java_type_to_str(&anon_rust_to_java(ty, context, false)?),
}
}
fn anon_rust_to_java(
ty: &syn::Type,
context: &Context,
use_type_map: bool,
) -> Result<JavaType, Error> {
match ty {
syn::Type::BareFn(..) => Err(Error {
level: Level::Error,
span: None, message: "C function pointers must have a name or function declaration \
associated with them"
.into(),
}),
syn::Type::Ptr(ref ptr) => {
let ty_str = ptr.into_token_stream().to_string();
if ty_str.as_str() == "* const c_char" || ty_str.as_str() == "* mut c_char" {
return Ok(JavaType::Object("String".into()));
}
anon_rust_to_java(&*ptr.elem, context, use_type_map)
}
syn::Type::Path(ref path) => path_to_java(&path.path, context, use_type_map),
_ => {
let new_type = ty.to_owned().into_token_stream().to_string();
if new_type == "()" {
Ok(JavaType::Primitive(Primitive::Void))
} else {
Err(Error {
level: Level::Error,
span: None, message: format!("unknown type `{}`", new_type),
})
}
}
}
}
fn path_to_java(
path: &syn::Path,
context: &Context,
use_type_map: bool,
) -> Result<JavaType, Error> {
if path.segments.is_empty() {
return Err(Error {
level: Level::Bug,
span: None, message: "invalid type".into(),
});
}
if path.segments.len() > 1 {
let mut path = path.clone();
let ty = unwrap!(path.segments.pop()).into_value().ident.to_string();
let module = path
.segments
.iter()
.map(|segment| segment.ident.to_string())
.collect::<Vec<_>>()
.join("::");
match module.as_str() {
"std::os::raw" | "libc" => {
Ok(rust_ty_to_java(ty.as_str()).unwrap_or(JavaType::Object(ty)))
}
ty => Err(Error {
level: Level::Error,
span: None,
message: format!("can't convert type {:?}", ty),
}),
}
} else {
let ty: String = path.segments[0].ident.to_owned().to_string();
let mapped = rust_ty_to_java(ty.as_str()).unwrap_or_else(|| {
if !use_type_map {
return JavaType::Object(struct_to_java_classname(ty));
}
if let Some(mapping) = context.type_map.get(ty.as_str()) {
(*mapping).clone()
} else {
JavaType::Object(struct_to_java_classname(ty))
}
});
Ok(mapped)
}
}
pub fn rust_ty_to_java(ty: &str) -> Option<JavaType> {
match ty {
"c_void" | "()" => Some(JavaType::Primitive(Primitive::Void)), "c_bool" | "bool" => Some(JavaType::Primitive(Primitive::Boolean)), "c_float" | "f32" => Some(JavaType::Primitive(Primitive::Float)), "c_double" | "f64" => Some(JavaType::Primitive(Primitive::Double)), "c_char" | "c_schar" | "c_uchar" | "u8" | "i8" => {
Some(JavaType::Primitive(Primitive::Byte))
} "c_short" | "c_ushort" | "u16" | "i16" => Some(JavaType::Primitive(Primitive::Short)),
"c_int" | "c_uint" | "u32" | "i32" => Some(JavaType::Primitive(Primitive::Int)), "c_long" | "c_ulong" | "u64" | "i64" | "usize" | "isize" => {
Some(JavaType::Primitive(Primitive::Long))
} _ => None, }
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::ty;
use jni::signature::{JavaType, Primitive};
#[test]
fn test_rust_to_java() {
let context = Context::default();
assert_eq!(
unwrap!(rust_to_java(&ty("*const c_char"), &context)),
JavaType::Object("String".to_string())
);
}
#[test]
fn libc_types() {
let context = Context::default();
let type_map = [
("libc::c_void", JavaType::Primitive(Primitive::Void)),
("libc::c_float", JavaType::Primitive(Primitive::Float)),
("libc::c_double", JavaType::Primitive(Primitive::Double)),
("libc::c_char", JavaType::Primitive(Primitive::Byte)),
("libc::c_schar", JavaType::Primitive(Primitive::Byte)),
("libc::c_uchar", JavaType::Primitive(Primitive::Byte)),
("libc::c_short", JavaType::Primitive(Primitive::Short)),
("libc::c_ushort", JavaType::Primitive(Primitive::Short)),
("libc::c_int", JavaType::Primitive(Primitive::Int)),
("libc::c_uint", JavaType::Primitive(Primitive::Int)),
("libc::c_long", JavaType::Primitive(Primitive::Long)),
("libc::c_ulong", JavaType::Primitive(Primitive::Long)),
];
for (rust_type, correct_java_type) in &type_map {
assert_eq!(
unwrap!(rust_to_java(&ty(rust_type), &context)),
*correct_java_type
);
}
}
#[test]
fn java_types_to_string() {
assert_eq!(
unwrap!(java_type_to_str(&JavaType::Object("String".to_string()))).as_str(),
"String"
);
assert_eq!(
unwrap!(java_type_to_str(&JavaType::Object(
"net.maidsafe.Test".to_string()
),))
.as_str(),
"net.maidsafe.Test"
);
assert_eq!(
unwrap!(java_type_to_str(&JavaType::Array(Box::new(
JavaType::Primitive(Primitive::Byte)
),)))
.as_str(),
"byte[]"
);
}
}