-use anyhow::Error;
-
use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;
+use syn::Error;
use crate::attribs::FunctionAttrs;
attr: FunctionAttrs,
mut func: syn::ItemFn,
mangled_package_name: Option<&str>,
+ export_public: bool,
) -> Result<XSub, Error> {
if !func.sig.generics.params.is_empty() {
bail!(&func.sig.generics => "generic functions cannot be exported as xsubs");
let deserialized_name =
Ident::new(&format!("deserialized_arg_{}", arg_name), arg_name.span());
- let missing_message =
- syn::LitStr::new("missing required parameter: '{}'\n", arg_name.span());
+ let missing_message = syn::LitStr::new(
+ &format!("missing required parameter: '{}'\n", arg_name),
+ arg_name.span(),
+ );
- extract_arguments.extend(quote! {
- let #extracted_name: ::perlmod::Value = match args.next() {
- Some(arg) => ::perlmod::Value::from(arg),
- None => {
+ let none_handling = if is_option_type(arg_type).is_some() {
+ quote! { ::perlmod::Value::new_undef(), }
+ } else {
+ quote! {
+ {
return Err(::perlmod::Value::new_string(#missing_message)
.into_mortal()
.into_raw());
}
+ }
+ };
+
+ extract_arguments.extend(quote! {
+ let #extracted_name: ::perlmod::Value = match args.next() {
+ Some(arg) => ::perlmod::Value::from(arg),
+ None => #none_handling
};
});
&xs_name,
&impl_xs_name,
passed_arguments,
+ export_public,
)?;
let tokens = quote! {
#[inline(never)]
#[allow(non_snake_case)]
- fn #impl_xs_name(
- _cv: &::perlmod::ffi::CV,
- ) -> Result<#return_type, *mut ::perlmod::ffi::SV> {
+ fn #impl_xs_name() -> Result<#return_type, *mut ::perlmod::ffi::SV> {
let argmark = unsafe { ::perlmod::ffi::pop_arg_mark() };
let mut args = argmark.iter();
xs_name: &Ident,
impl_xs_name: &Ident,
passed_arguments: TokenStream,
+ export_public: bool,
) -> Result<ReturnHandling, Error> {
let return_type;
let mut handle_return;
let wrapper_func;
+ let pthx = crate::pthx_param();
match ret {
Return::None(result) => {
return_type = quote! { () };
};
}
+ let vis = if export_public {
+ quote! { #[no_mangle] pub }
+ } else {
+ quote! { #[allow(non_snake_case)] }
+ };
+
wrapper_func = quote! {
- #[no_mangle]
#[doc(hidden)]
- pub extern "C" fn #xs_name(cv: &::perlmod::ffi::CV) {
+ #vis extern "C" fn #xs_name(#pthx _cv: &::perlmod::ffi::CV) {
unsafe {
- match #impl_xs_name(cv) {
+ match #impl_xs_name() {
Ok(()) => (),
Err(sv) => ::perlmod::ffi::croak(sv),
}
wrapper_func = quote! {
#[no_mangle]
#[doc(hidden)]
- pub extern "C" fn #xs_name(cv: &::perlmod::ffi::CV) {
+ pub extern "C" fn #xs_name(#pthx _cv: &::perlmod::ffi::CV) {
unsafe {
- match #impl_xs_name(cv) {
+ match #impl_xs_name() {
Ok(sv) => ::perlmod::ffi::stack_push_raw(sv),
Err(sv) => ::perlmod::ffi::croak(sv),
}
};
}
+ let mut rt = TokenStream::new();
if attr.raw_return {
- let mut rt = TokenStream::new();
for i in 0..count {
let i = simple_usize(i, Span::call_site());
rt.extend(quote! { (result.#i).into_mortal().into_raw(), });
}
- handle_return.extend(quote! {
- Ok((#rt))
- });
} else {
- let mut rt = TokenStream::new();
for i in 0..count {
let i = simple_usize(i, Span::call_site());
rt.extend(quote! {
},
});
}
- handle_return.extend(quote! {
- Ok((#rt))
- });
}
+ handle_return.extend(quote! {
+ Ok((#rt))
+ });
+ drop(rt);
let icount = simple_usize(count, Span::call_site());
let sp_offset = simple_usize(count - 1, Span::call_site());
wrapper_func = quote! {
#[no_mangle]
#[doc(hidden)]
- pub extern "C" fn #xs_name(cv: &::perlmod::ffi::CV) {
+ pub extern "C" fn #xs_name(#pthx _cv: &::perlmod::ffi::CV) {
unsafe {
- match #impl_xs_name(cv) {
+ match #impl_xs_name() {
Ok(sv) => { #push },
Err(sv) => ::perlmod::ffi::croak(sv),
}
}
let segs = &p.path.segments;
let is_result = match segs.len() {
- 1 => segs.last().unwrap().ident == "Result",
- 2 => segs.first().unwrap().ident == "std" && segs.last().unwrap().ident == "Result",
+ 1 => segs[0].ident == "Result",
+ 3 => segs[0].ident == "std" && segs[1].ident == "result" && segs[2].ident == "Result",
_ => false,
};
if !is_result {
fn simple_usize(i: usize, span: Span) -> syn::LitInt {
syn::LitInt::new(&format!("{}", i), span)
}
+
+/// Note that we cannot handle renamed imports at all here...
+pub fn is_option_type(ty: &syn::Type) -> Option<&syn::Type> {
+ if let syn::Type::Path(p) = ty {
+ if p.qself.is_some() {
+ return None;
+ }
+ let segs = &p.path.segments;
+ let is_option = match segs.len() {
+ 1 => segs[0].ident == "Option",
+ 3 => segs[0].ident == "std" && segs[1].ident == "option" && segs[2].ident == "Option",
+ _ => false,
+ };
+ if !is_option {
+ return None;
+ }
+
+ if let syn::PathArguments::AngleBracketed(generic) = &segs.last().unwrap().arguments {
+ if generic.args.len() != 1 {
+ return None;
+ }
+
+ if let syn::GenericArgument::Type(ty) = generic.args.first().unwrap() {
+ return Some(ty);
+ }
+ }
+ }
+ None
+}