ffi-convert-derive 0.7.0

Macros implementations of CReprOf, AsRust, CDrop traits from ffi-convert
Documentation
use proc_macro::TokenStream;

use quote::quote;

use crate::utils::{
    parse_enum_variants, parse_struct_fields, parse_target_type, Field, TypeArrayOrTypePath,
};

pub fn impl_creprof_macro(input: &syn::DeriveInput) -> TokenStream {
    let name = &input.ident;
    let target_type = parse_target_type(&input.attrs);

    match &input.data {
        syn::Data::Struct(data_struct) => impl_creprof_struct(name, &target_type, data_struct),
        syn::Data::Enum(data_enum) => impl_creprof_enum(name, &target_type, data_enum),
        _ => panic!("CReprOf can only be derived for structs and unit enums"),
    }
}

fn impl_creprof_struct(
    struct_name: &syn::Ident,
    target_type: &syn::Path,
    data: &syn::DataStruct,
) -> TokenStream {
    let fields = parse_struct_fields(data);
    let c_repr_of_fields = fields
        .iter()
        .map(|field| {
            let Field {
                name: field_name,
                target_name: target_field_name,
                ref field_type,
                ..
            } = field;

            let mut conversion = if field.is_string {
                quote!(std::ffi::CString::c_repr_of(field)?)
            } else {
                match field_type {
                    TypeArrayOrTypePath::TypeArray(type_array) => {
                        quote!(<#type_array>::c_repr_of(field)?)
                    }
                    TypeArrayOrTypePath::TypePath(type_path) => {
                        quote!(#type_path::c_repr_of(field)?)
                    }
                }
            };

            if field.is_pointer {
                for _ in 0..field.levels_of_indirection {
                    conversion = quote!(#conversion.into_raw_pointer())
                }
            }

            conversion = if field.is_nullable {
                quote!(
                    #field_name: if let Some(field) = input.#target_field_name {
                        #conversion
                    } else {
                        std::ptr::null() as _
                    }
                )
            } else {
                quote!(#field_name: { let field = input.#target_field_name ; #conversion })
            };
            if let Some(convert) = &field.c_repr_of_convert {
                quote!(#field_name: #convert)
            } else {
                conversion
            }
        })
        .collect::<Vec<_>>();

    quote!(
        impl CReprOf<#target_type> for #struct_name {
            fn c_repr_of(input: #target_type) -> Result<Self, ffi_convert::CReprOfError> {
                use ffi_convert::RawPointerConverter;
                Ok(Self {
                    #(#c_repr_of_fields,)*
                })
            }
        }
    )
    .into()
}

fn impl_creprof_enum(
    enum_name: &syn::Ident,
    target_type: &syn::Path,
    data: &syn::DataEnum,
) -> TokenStream {
    let variants = parse_enum_variants(data);

    let match_arms = variants
        .iter()
        .map(|variant| quote!(#target_type::#variant => Ok(#enum_name::#variant)));

    quote!(
        impl CReprOf<#target_type> for #enum_name {
            fn c_repr_of(input: #target_type) -> Result<Self, ffi_convert::CReprOfError> {
                match input {
                    #(#match_arms,)*
                }
            }
        }
    )
    .into()
}