ezffi-macros 0.1.1

Proc-macros backing the ezffi crate
Documentation
use quote::quote;
use syn::ItemEnum;

use crate::{Namer, structs::expand_type};

pub fn is_c_compatible_enum(item: &ItemEnum) -> bool {
    item.generics.gt_token.is_none()
        && item
            .variants
            .iter()
            .all(|v| matches!(v.fields, syn::Fields::Unit))
}

pub fn expand_enum(item: &ItemEnum) -> syn::Result<proc_macro2::TokenStream> {
    if item.generics.gt_token.is_some() {
        return Err(syn::Error::new_spanned(
            &item.generics,
            "generic enums are not supported by #[ezffi::export]",
        ));
    }

    if let Some(lt) = item.generics.lifetimes().next() {
        return Err(syn::Error::new_spanned(
            lt,
            "lifetime parameters are not supported by #[ezffi::export]",
        ));
    }

    // Enums no C-compatibles are wrapped in a struct behind *mut c_void, the same
    // way no C-compatible structs are exposed
    Ok(expand_type(&item.ident))
}

pub fn expand_c_enum(item: &ItemEnum) -> proc_macro2::TokenStream {
    let rust_ty_name = &item.ident;
    let c_ty_name = Namer::name_struct(rust_ty_name);
    let variants = &item.variants;
    let attrs = &item.attrs;

    let has_repr = attrs.iter().any(|a| a.path().is_ident("repr"));
    let repr = if has_repr {
        quote! {}
    } else {
        quote! { #[repr(C)] }
    };

    quote! {
        #[derive(Clone, Copy)]
        #repr
        #(#attrs)*
        pub enum #c_ty_name {
            #variants
        }

        pub type #rust_ty_name = #c_ty_name;

        impl ::ezffi::RustRefIntoC<()> for #c_ty_name {
            type C = #c_ty_name;

            unsafe fn ref_into_c(&self) -> Self::C { *self }
        }

        impl ::ezffi::RustOwnedIntoC<()> for #c_ty_name {
            type C = #c_ty_name;

            unsafe fn owned_into_c(self) -> Self::C { self }
        }

        impl ::ezffi::CRefIntoRust<#c_ty_name> for #c_ty_name {
            unsafe fn into_rust(&self) -> &#c_ty_name { self }
            unsafe fn into_rust_mut(&mut self) -> &mut #c_ty_name { self }
        }

        impl ::ezffi::COwnedIntoRust<#c_ty_name> for #c_ty_name {
            unsafe fn into_rust_owned(self) -> #c_ty_name { self }
        }
    }
}