use proc_macro2::{Span, TokenStream}; use quote::ToTokens; use syn::{ReturnType, Type}; use crate::ext::bare_type; #[derive(Clone)] 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, pointer: false }), syn::ReturnType::Type(_tok, ty) => match bare_type(ty.clone()) { Some(path) => { let Some(last) = path.path.segments.last() else { return Err(syn::Error::new(Span::call_site(), "empty Result type is not valid")); }; if last.ident == "Result" { match &last.arguments { syn::PathArguments::None => return Err(syn::Error::new(Span::call_site(), "Result without generics is not valid")), syn::PathArguments::Parenthesized(_) => return Err(syn::Error::new(Span::call_site(), "Parenthesized Result is not valid")), syn::PathArguments::AngleBracketed(ref generics) => for generic in generics.args.iter() { match generic { syn::GenericArgument::Lifetime(_) => continue, 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")) } } } } 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 }) }, None => Err(syn::Error::new(Span::call_site(), "unsupported return type")), }, } } 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) => quote::quote!( -> <#t as jni_toolbox::IntoJava<'local>>::Ret ) } } } fn is_void(ty: &syn::Type) -> bool { match ty { Type::Array(_) => false, Type::BareFn(_) => false, Type::Group(g) => is_void(&g.elem), Type::ImplTrait(_) => false, Type::Infer(_) => false, Type::Macro(_) => false, Type::Never(_) => true, Type::Paren(p) => is_void(&p.elem), Type::Path(p) => p.path.segments.is_empty(), Type::Ptr(_) => false, Type::Reference(_) => false, Type::Slice(_) => false, Type::TraitObject(_) => false, Type::Tuple(x) => x.elems.is_empty(), Type::Verbatim(_) => false, _ => todo!(), } }