use syn::{Generics, Ident, ItemStruct};
use crate::Namer;
use quote::quote;
pub fn is_c_compatible_struct(item: &ItemStruct) -> bool {
super::FFITypeResolver::is_rust_type_c_compatible(&item.ident.to_string())
}
pub fn expand_struct(item: &ItemStruct) -> syn::Result<proc_macro2::TokenStream> {
if let Some(lt) = item.generics.lifetimes().next() {
return Err(syn::Error::new_spanned(
lt,
"lifetime parameters are not supported by #[ezffi::export]",
));
}
if item.generics.gt_token.is_some() {
Ok(expand_generic_type(&item.ident, &item.generics))
} else {
Ok(expand_type(&item.ident))
}
}
pub fn expand_c_struct(item: &ItemStruct) -> proc_macro2::TokenStream {
let rust_ty_name = &item.ident;
let c_ty_name = Namer::name_struct(rust_ty_name);
let fields = &item.fields;
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)] }
};
let semi = match fields {
syn::Fields::Named(_) => quote! {},
_ => quote! { ; },
};
quote! {
#[derive(Clone, Copy)]
#repr
#(#attrs)*
pub struct #c_ty_name #fields #semi
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 }
}
}
}
pub fn expand_type(ty_name: &Ident) -> proc_macro2::TokenStream {
let c_ty_name = Namer::name_struct(ty_name);
let free_fn_name = Namer::name_free_fn(ty_name);
quote! {
#[doc(hidden)]
#[derive(Clone, Copy)]
#[repr(C)]
pub struct #c_ty_name {
inner: *mut core::ffi::c_void,
}
const _: () = {
impl ::ezffi::RustRefIntoC<()> for #ty_name {
type C = #c_ty_name;
#[inline]
unsafe fn ref_into_c(&self) -> Self::C {
#c_ty_name {
inner: self as *const Self as *mut core::ffi::c_void,
}
}
}
impl ::ezffi::RustOwnedIntoC<()> for #ty_name {
type C = #c_ty_name;
#[inline]
unsafe fn owned_into_c(self) -> Self::C {
#c_ty_name {
inner: Box::into_raw(Box::new(self)) as *mut core::ffi::c_void,
}
}
}
};
impl<T> ::ezffi::CRefIntoRust<T> for #c_ty_name {
#[inline]
unsafe fn into_rust(&self) -> &T {
unsafe { &*(self.inner as *mut T) }
}
#[inline]
unsafe fn into_rust_mut(&mut self) -> &mut T {
unsafe { &mut *(self.inner as *mut T) }
}
}
impl<T> ::ezffi::COwnedIntoRust<T> for #c_ty_name {
#[inline]
unsafe fn into_rust_owned(self) -> T {
unsafe { *Box::from_raw(self.inner as *mut T) }
}
}
#[doc(hidden)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn #free_fn_name(o: *const #c_ty_name) {
let _ = unsafe { Box::from_raw((*o).inner as *mut #ty_name) };
}
}
}
fn expand_generic_type(ty_name: &Ident, generics: &Generics) -> proc_macro2::TokenStream {
let c_ty_name = Namer::name_struct(ty_name);
let free_fn_name = Namer::name_free_fn(ty_name);
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let turbofish = ty_generics.as_turbofish();
quote! {
#[doc(hidden)]
#[derive(Clone, Copy)]
#[repr(C)]
pub struct #c_ty_name {
inner: *mut core::ffi::c_void,
drop_fn: unsafe extern "C" fn(*mut core::ffi::c_void),
}
const _: () = {
unsafe extern "C" fn __ezffi_drop #impl_generics (
p: *mut core::ffi::c_void,
) #where_clause {
let _ = unsafe { Box::from_raw(p as *mut #ty_name #ty_generics) };
}
impl #impl_generics ::ezffi::RustRefIntoC<()>
for #ty_name #ty_generics #where_clause
{
type C = #c_ty_name;
#[inline]
unsafe fn ref_into_c(&self) -> Self::C {
#c_ty_name {
inner: self as *const Self as *mut core::ffi::c_void,
drop_fn: __ezffi_drop #turbofish,
}
}
}
impl #impl_generics ::ezffi::RustOwnedIntoC<()>
for #ty_name #ty_generics #where_clause
{
type C = #c_ty_name;
#[inline]
unsafe fn owned_into_c(self) -> Self::C {
#c_ty_name {
inner: Box::into_raw(Box::new(self)) as *mut core::ffi::c_void,
drop_fn: __ezffi_drop #turbofish,
}
}
}
};
impl<T> ::ezffi::CRefIntoRust<T> for #c_ty_name {
#[inline]
unsafe fn into_rust(&self) -> &T {
unsafe { &*(self.inner as *mut T) }
}
#[inline]
unsafe fn into_rust_mut(&mut self) -> &mut T {
unsafe { &mut *(self.inner as *mut T) }
}
}
impl<T> ::ezffi::COwnedIntoRust<T> for #c_ty_name {
#[inline]
unsafe fn into_rust_owned(self) -> T {
unsafe { *Box::from_raw(self.inner as *mut T) }
}
}
#[doc(hidden)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn #free_fn_name(o: *const #c_ty_name) {
unsafe { ((*o).drop_fn)((*o).inner); }
}
}
}