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
94
95
96
97
use ::heck::SnakeCase;
use crate::helpers::*;

impl ::quote::ToTokens for crate::News {
    fn to_tokens(&self, tokens: &mut ::proc_macro2::TokenStream) {
        let new_funcs = &self.0;
        tokens.extend(::quote::quote! {
            #(#new_funcs)*
        });
    }
}

impl<'a> From<&'a crate::Data> for crate::News {
    fn from(data: &'a crate::Data) -> Self {
        Self(
            if data.opaque {
                vec![]
            } else {
                let orig_name = &data.ident;
                let ffi_name = orig_name.clone().prefix("FFI");

                match &data.data {
                    ::darling::ast::Data::Enum(variants) => enum_new_funcs(
                        &orig_name,
                        &ffi_name,
                        variants,
                    ),
                    ::darling::ast::Data::Struct(fields) => struct_new_func(
                        &orig_name,
                        &::syn::parse_quote! { #ffi_name },
                        None,
                        fields,
                    ).map(|new| vec![new]).unwrap_or_else(|| vec![]),
                }
            }
        )
    }
}

fn enum_new_funcs(
    orig_name: &::syn::Ident,
    ffi_name: &::syn::Ident,
    variants: &Vec<crate::Variant>,
) -> Vec<::syn::ItemFn> {
    variants.iter().filter_map(|v| {
        let variant_ident = &v.ident;
        let orig_enum_variant_name = new_ident(&format!("{}{}", orig_name, variant_ident));

        let ffi_path = ::syn::parse_quote!{ #ffi_name };
        let variant_path = ::syn::parse_quote!{ #variant_ident };
        struct_new_func(&orig_enum_variant_name, &ffi_path, Some(&variant_path), &v.fields)
    }).collect()
}

fn struct_new_func(
    orig_name: &::syn::Ident,
    ffi_name: &::syn::Path,
    variant_path: Option<&::syn::Path>,
    fields: &::darling::ast::Fields<crate::Field>,
) -> Option<::syn::ItemFn> {
    if fields.iter().any(|field| field.opaque) {
        None
    } else {
        let func_name = new_ident(&format!("new_{}", orig_name.to_string().to_snake_case()));

        let field_decls: Vec<::syn::FnArg> = fields.iter().enumerate().map(|(idx, field)| {
            let ident = field.ident.clone().unwrap_or_else(|| idx_to_name(idx as u32));
            let ty = crate::types::switch(&field.ty).fold(field.ty.clone());
            ::syn::parse_quote! { #ident: #ty }
        }).collect();

        let field_names: Vec<::syn::Ident> = fields.iter().enumerate().map(|(idx, field)| {
            field.ident.clone().unwrap_or_else(|| idx_to_name(idx as u32))
        }).collect();

        let variant_ts: Option<::proc_macro2::TokenStream> = variant_path.map(|vn| {
            ::quote::quote! { ::#vn }
        });

        let init_expr: ::syn::Expr = match fields.style {
            ::darling::ast::Style::Tuple => ::syn::parse_quote! {
                #ffi_name#variant_ts(#(#field_names),*)
            },
            ::darling::ast::Style::Struct => ::syn::parse_quote! {
                #ffi_name#variant_ts{#(#field_names),*}
            },
            ::darling::ast::Style::Unit => ::syn::parse_quote! { #ffi_name#variant_ts },
        };

        Some(::syn::parse_quote! {
            #[no_mangle]
            pub extern "C" fn #func_name(#(#field_decls),*) -> *mut #ffi_name {
                Box::into_raw(Box::new(#init_expr))
            }
        })
    }
}