]> git.proxmox.com Git - perlmod.git/blobdiff - perlmod-macro/src/function.rs
add ability to set the errno value
[perlmod.git] / perlmod-macro / src / function.rs
index 016703cdb87eab16862cd38928b8d0c745aabb15..52aec1f1a6e97eb90db8b571890aeb428821abeb 100644 (file)
@@ -1,8 +1,8 @@
 use proc_macro2::{Ident, Span, TokenStream};
 
 use quote::quote;
-use syn::Error;
 use syn::spanned::Spanned;
+use syn::Error;
 
 use crate::attribs::FunctionAttrs;
 
@@ -52,15 +52,20 @@ impl ArgumentAttrs {
     }
 }
 
-enum Return {
+struct Return {
+    result: bool,
+    value: ReturnValue,
+}
+
+enum ReturnValue {
     /// Return nothing. (This is different from returning an implicit undef!)
-    None(bool),
+    None,
 
     /// Return a single element.
-    Single(bool),
+    Single,
 
     /// We support tuple return types. They act like "list" return types in perl.
-    Tuple(bool, usize),
+    Tuple(usize),
 }
 
 pub fn handle_function(
@@ -78,15 +83,15 @@ pub fn handle_function(
     }
 
     let name = func.sig.ident.clone();
-    let export_public = export_public.then(|| &func.vis);
+    let export_public = export_public.then_some(&func.vis);
     let xs_name = attr
         .xs_name
         .clone()
         .unwrap_or_else(|| match mangled_package_name {
-            None => Ident::new(&format!("xs_{}", name), name.span()),
-            Some(prefix) => Ident::new(&format!("xs_{}_{}", prefix, name), name.span()),
+            None => Ident::new(&format!("xs_{name}"), name.span()),
+            Some(prefix) => Ident::new(&format!("xs_{prefix}_{name}"), name.span()),
         });
-    let impl_xs_name = Ident::new(&format!("impl_xs_{}", name), name.span());
+    let impl_xs_name = Ident::new(&format!("impl_xs_{name}"), name.span());
 
     let mut trailing_options = 0;
     let mut extract_arguments = TokenStream::new();
@@ -134,12 +139,12 @@ pub fn handle_function(
             continue;
         }
 
-        let extracted_name = Ident::new(&format!("extracted_arg_{}", arg_name), arg_name.span());
+        let extracted_name = Ident::new(&format!("extracted_arg_{arg_name}"), arg_name.span());
         let deserialized_name =
-            Ident::new(&format!("deserialized_arg_{}", arg_name), arg_name.span());
+            Ident::new(&format!("deserialized_arg_{arg_name}"), arg_name.span());
 
         let missing_message = syn::LitStr::new(
-            &format!("missing required parameter: '{}'\n", arg_name),
+            &format!("missing required parameter: '{arg_name}'\n"),
             arg_name.span(),
         );
 
@@ -203,11 +208,23 @@ pub fn handle_function(
     }
 
     let has_return_value = match &func.sig.output {
-        syn::ReturnType::Default => Return::None(false),
-        syn::ReturnType::Type(_arrow, ty) => match get_result_type(&**ty) {
-            (syn::Type::Tuple(tuple), result) if tuple.elems.is_empty() => Return::None(result),
-            (syn::Type::Tuple(tuple), result) => Return::Tuple(result, tuple.elems.len()),
-            (_, result) => Return::Single(result),
+        syn::ReturnType::Default => Return {
+            result: false,
+            value: ReturnValue::None,
+        },
+        syn::ReturnType::Type(_arrow, ty) => match get_result_type(ty) {
+            (syn::Type::Tuple(tuple), result) if tuple.elems.is_empty() => Return {
+                result,
+                value: ReturnValue::None,
+            },
+            (syn::Type::Tuple(tuple), result) => Return {
+                result,
+                value: ReturnValue::Tuple(tuple.elems.len()),
+            },
+            (_, result) => Return {
+                result,
+                value: ReturnValue::Single,
+            },
         },
     };
 
@@ -254,7 +271,7 @@ pub fn handle_function(
                     .into_raw());
             }
 
-            drop(args);
+            //drop(args);
 
             #deserialized_arguments
 
@@ -320,29 +337,57 @@ fn handle_return_kind(
     };
 
     let (cv_arg_name, cv_arg_passed) = if cv_arg {
-        (quote! { cv }, quote! { cv })
+        (
+            quote! { cv },
+            quote! { ::perlmod::Value::from_raw_ref(cv as *mut ::perlmod::ffi::SV) },
+        )
     } else {
         (quote! { _cv }, TokenStream::new())
     };
 
+    let return_error = if ret.result {
+        if attr.serialize_error {
+            quote! {
+                match ::perlmod::to_value(&err) {
+                    Ok(err) => return Err(err.into_mortal().into_raw()),
+                    Err(err) => {
+                        return Err(::perlmod::Value::new_string(&format!("{}\n", err))
+                            .into_mortal()
+                            .into_raw());
+                    }
+                }
+            }
+        } else {
+            quote! {
+                return Err(::perlmod::Value::new_string(&format!("{}\n", err))
+                    .into_mortal()
+                    .into_raw());
+            }
+        }
+    } else {
+        TokenStream::new()
+    };
+
+    let copy_errno = if attr.errno {
+        quote! { ::perlmod::error::copy_errno_to_libc(); }
+    } else {
+        TokenStream::new()
+    };
+
     let pthx = crate::pthx_param();
-    match ret {
-        Return::None(result) => {
+    match ret.value {
+        ReturnValue::None => {
             return_type = quote! { () };
 
             if attr.raw_return {
                 bail!(&attr.raw_return => "raw_return attribute is illegal without a return value");
             }
 
-            if result {
+            if ret.result {
                 handle_return = quote! {
                     match #name(#passed_arguments) {
                         Ok(()) => (),
-                        Err(err) => {
-                            return Err(::perlmod::Value::new_string(&format!("{}\n", err))
-                                .into_mortal()
-                                .into_raw());
-                        }
+                        Err(err) => { #return_error }
                     }
 
                     Ok(())
@@ -357,9 +402,11 @@ fn handle_return_kind(
 
             wrapper_func = quote! {
                 #[doc(hidden)]
-                #vis extern "C" fn #xs_name(#pthx #cv_arg_name: &::perlmod::ffi::CV) {
+                #vis extern "C" fn #xs_name(#pthx #cv_arg_name: *mut ::perlmod::ffi::CV) {
                     unsafe {
-                        match #impl_xs_name(#cv_arg_passed) {
+                        let res = #impl_xs_name(#cv_arg_passed);
+                        #copy_errno
+                        match res {
                             Ok(()) => (),
                             Err(sv) => ::perlmod::ffi::croak(sv),
                         }
@@ -367,18 +414,14 @@ fn handle_return_kind(
                 }
             };
         }
-        Return::Single(result) => {
+        ReturnValue::Single => {
             return_type = quote! { *mut ::perlmod::ffi::SV };
 
-            if result {
+            if ret.result {
                 handle_return = quote! {
                     let result = match #name(#passed_arguments) {
                         Ok(output) => output,
-                        Err(err) => {
-                            return Err(::perlmod::Value::new_string(&format!("{}\n", err))
-                                .into_mortal()
-                                .into_raw());
-                        }
+                        Err(err) => { #return_error }
                     };
                 };
             } else {
@@ -404,9 +447,11 @@ fn handle_return_kind(
 
             wrapper_func = quote! {
                 #[doc(hidden)]
-                #vis extern "C" fn #xs_name(#pthx #cv_arg_name: &::perlmod::ffi::CV) {
+                #vis extern "C" fn #xs_name(#pthx #cv_arg_name: *mut ::perlmod::ffi::CV) {
                     unsafe {
-                        match #impl_xs_name(#cv_arg_passed) {
+                        let res = #impl_xs_name(#cv_arg_passed);
+                        #copy_errno
+                        match res {
                             Ok(sv) => ::perlmod::ffi::stack_push_raw(sv),
                             Err(sv) => ::perlmod::ffi::croak(sv),
                         }
@@ -414,7 +459,7 @@ fn handle_return_kind(
                 }
             };
         }
-        Return::Tuple(result, count) => {
+        ReturnValue::Tuple(count) => {
             return_type = {
                 let mut rt = TokenStream::new();
                 for _ in 0..count {
@@ -423,20 +468,16 @@ fn handle_return_kind(
                 quote! { (#rt) }
             };
 
-            if result {
+            if ret.result {
                 handle_return = quote! {
                     let result = match #name(#passed_arguments) {
                         Ok(output) => output,
-                        Err(err) => {
-                            return Err(::perlmod::Value::new_string(&format!("{}\n", err))
-                                .into_mortal()
-                                .into_raw());
-                        }
+                        Err(err) => { #return_error }
                     };
                 };
             } else {
                 handle_return = quote! {
-                    let result = match #name(#passed_arguments);
+                    let result = #name(#passed_arguments);
                 };
             }
 
@@ -490,9 +531,11 @@ fn handle_return_kind(
 
             wrapper_func = quote! {
                 #[doc(hidden)]
-                #vis extern "C" fn #xs_name(#pthx #cv_arg_name: &::perlmod::ffi::CV) {
+                #vis extern "C" fn #xs_name(#pthx #cv_arg_name: *mut ::perlmod::ffi::CV) {
                     unsafe {
-                        match #impl_xs_name(#cv_arg_passed) {
+                        let res = #impl_xs_name(#cv_arg_passed);
+                        #copy_errno
+                        match res {
                             Ok(sv) => { #push },
                             Err(sv) => ::perlmod::ffi::croak(sv),
                         }
@@ -550,7 +593,7 @@ pub fn get_result_type(ty: &syn::Type) -> (&syn::Type, bool) {
 
 /// Get a non-suffixed integer from an usize.
 fn simple_usize(i: usize, span: Span) -> syn::LitInt {
-    syn::LitInt::new(&format!("{}", i), span)
+    syn::LitInt::new(&format!("{i}"), span)
 }
 
 /// Note that we cannot handle renamed imports at all here...