1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
use ::heck::SnakeCase;
use crate::helpers::*;

impl ::quote::ToTokens for crate::Free {
    fn to_tokens(&self, tokens: &mut ::proc_macro2::TokenStream) {
        let func_name = &self.func_name;
        let receiver = &self.receiver;
        let ffi_type = &self.ffi_type;
        let free_expr = &self.free_expr;

        tokens.extend(::quote::quote! {
            #[no_mangle]
            pub extern "C" fn #func_name(#receiver: #ffi_type) {
                if !#receiver.is_null() {
                    let #receiver = *unsafe { Box::from_raw(#receiver) };
                    #free_expr;
                }
            }
        });
    }
}

impl<'a> From<&'a crate::Data> for crate::Free {
    fn from(data: &'a crate::Data) -> Self {
        let ffi_name = data.ident.clone().prefix("FFI");

        let func_name = new_ident(&format!("free_{}", data.ident.to_string().to_snake_case()));
        let receiver = ::syn::parse_quote! { tmp };
        let ffi_type = ::syn::parse_quote! { *mut #ffi_name };
        let free_expr = if data.opaque {
            ::syn::parse_quote! { {} }
        } else {
            match &data.data {
                ::darling::ast::Data::Enum(variants) => enum_free_expr(
                    &::syn::parse_quote! { #ffi_name },
                    &receiver,
                    variants,
                ),
                ::darling::ast::Data::Struct(fields) => struct_free_expr(
                    Some(&receiver),
                    fields,
                ),
            }
        };

        Self{func_name, receiver, ffi_type, free_expr}
    }
}

fn enum_free_expr(
    ffi_name: &::syn::Path,
    receiver: &::syn::Expr,
    variants: &Vec<crate::Variant>,
) -> ::syn::Expr {
    let arms: Vec<::syn::Arm> = variants.iter().map(|v| {
        let variant_name = &v.ident;
        let ffi_variant_fullpath: ::syn::Path = ::syn::parse_quote! { #ffi_name::#variant_name };

        let free_expr = struct_free_expr(None, &v.fields);
        let destructuring: Vec<::syn::Ident> = v.fields.iter().enumerate().map(|(idx, field)| {
            field.ident.clone().unwrap_or(idx_to_name(idx as u32))
        }).collect();

        match v.fields.style {
            ::darling::ast::Style::Tuple => ::syn::parse_quote! {
                #ffi_variant_fullpath(#(#destructuring),*) => #free_expr
            },
            ::darling::ast::Style::Struct => ::syn::parse_quote! {
                #ffi_variant_fullpath{#(#destructuring),*} => #free_expr
            },
            ::darling::ast::Style::Unit => ::syn::parse_quote! {
                #ffi_variant_fullpath => {}
            },
        }
    }).collect();

    ::syn::parse_quote! {
        match #receiver {
            #(#arms),*
        }
    }
}

fn struct_free_expr(
    receiver: Option<&::syn::Expr>,
    fields: &::darling::ast::Fields<crate::Field>,
) -> ::syn::Expr {
    let field_exprs = fields.iter().enumerate().filter_map(|(idx, field)| {
        field.free(idx, receiver)
    });

    ::syn::parse_quote! { { #(#field_exprs);* } }
}