From 6f32a2814745d227189bfcca273594b76769d8ea Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 17 Jan 2022 18:48:46 +0100 Subject: [PATCH] Add initial derive(Encode, RefEncode) implementation --- objc2-encode/tests/test_derive.rs | 25 ++++++++++ objc2-proc-macros/src/derive.rs | 76 ++++++++++++++++++++++++++----- objc2-proc-macros/src/lib.rs | 1 + objc2-proc-macros/src/utils.rs | 30 ++++++++++++ 4 files changed, 121 insertions(+), 11 deletions(-) create mode 100644 objc2-proc-macros/src/utils.rs diff --git a/objc2-encode/tests/test_derive.rs b/objc2-encode/tests/test_derive.rs index ae2abbb7f..fe9f7f308 100644 --- a/objc2-encode/tests/test_derive.rs +++ b/objc2-encode/tests/test_derive.rs @@ -20,3 +20,28 @@ fn cgpoint() { assert_eq!(CGPoint::ENCODING, enc); assert_eq!(CGPoint::ENCODING_REF, Encoding::Pointer(&enc)); } + +#[derive(Encode, RefEncode)] +#[repr(transparent)] +struct Transparent { + _inner: usize, +} + +#[test] +fn transparent() { + assert_eq!(Transparent::ENCODING, usize::ENCODING); + assert_eq!(Transparent::ENCODING_REF, usize::ENCODING_REF); +} + +#[derive(Encode, RefEncode)] +#[repr(usize)] +enum MyEnum { + _A = 1, + _B = 2, +} + +#[test] +fn enum_repr() { + assert_eq!(MyEnum::ENCODING, usize::ENCODING); + assert_eq!(MyEnum::ENCODING_REF, usize::ENCODING_REF); +} diff --git a/objc2-proc-macros/src/derive.rs b/objc2-proc-macros/src/derive.rs index 4ee6aab4e..db1c9e97e 100644 --- a/objc2-proc-macros/src/derive.rs +++ b/objc2-proc-macros/src/derive.rs @@ -1,24 +1,78 @@ use proc_macro::TokenStream; use quote::quote; -use syn::DeriveInput; +use syn::{Data, DeriveInput}; + +use crate::utils::get_repr; pub(crate) fn impl_encode(ast: &DeriveInput) -> TokenStream { - let name = &ast.ident; - let gen = quote! { - unsafe impl ::objc2_encode::Encode for #name { - const ENCODING: ::objc2_encode::Encoding<'static> = ::objc2_encode::Encoding::Struct( - stringify!(#name), - &[CGFloat::ENCODING, CGFloat::ENCODING], - ); + let DeriveInput { + ident, data, attrs, .. + } = ast; + + let repr = match get_repr(&attrs) { + Some(repr) => repr, + None => panic!("Missing repr"), + }; + + let encoding = match data { + Data::Struct(data) => { + let fields = data + .fields + .iter() + .map(|field| &field.ty) + .collect::>(); + match &*repr.to_string() { + "transparent" => { + let field = fields[0]; + assert_eq!(fields.len(), 1, "Expected one item"); + quote!(<#field as ::objc2_encode::Encode>::ENCODING) + } + "C" => { + quote!( + ::objc2_encode::Encoding::Struct( + stringify!(#ident), + &[#(<#fields as ::objc2_encode::Encode>::ENCODING),*], + ) + ) + } + _ => panic!("Unknown repr"), + } } + Data::Enum(_) => { + let ty = match &*repr.to_string() { + "usize" => quote! { core::primitive::usize }, + "isize" => quote! { core::primitive::isize }, + "u64" => quote! { core::primitive::u64 }, + "i64" => quote! { core::primitive::i64 }, + "u32" => quote! { core::primitive::u32 }, + "i32" => quote! { core::primitive::i32 }, + "u16" => quote! { core::primitive::u16 }, + "i16" => quote! { core::primitive::i16 }, + "u8" => quote! { core::primitive::u8 }, + "i8" => quote! { core::primitive::i8 }, + _ => panic!("Unknown repr"), + }; + quote! { <#ty as ::objc2_encode::Encode>::ENCODING } + } + Data::Union(_) => unimplemented!(), }; - gen.into() + + // TODO: Generics + quote! { + unsafe impl ::objc2_encode::Encode for #ident { + const ENCODING: ::objc2_encode::Encoding<'static> = #encoding; + } + } + .into() } pub(crate) fn impl_ref_encode(ast: &DeriveInput) -> TokenStream { - let name = &ast.ident; + let DeriveInput { ident, .. } = ast; + // TODO: Generics + // TODO: Objects + let gen = quote! { - unsafe impl ::objc2_encode::RefEncode for #name { + unsafe impl ::objc2_encode::RefEncode for #ident { const ENCODING_REF: ::objc2_encode::Encoding<'static> = ::objc2_encode::Encoding::Pointer( &::ENCODING ); diff --git a/objc2-proc-macros/src/lib.rs b/objc2-proc-macros/src/lib.rs index 9572d3d60..5f13e13c5 100644 --- a/objc2-proc-macros/src/lib.rs +++ b/objc2-proc-macros/src/lib.rs @@ -20,6 +20,7 @@ extern "C" {} use proc_macro::TokenStream; mod derive; +mod utils; /// TODO #[proc_macro_derive(Encode)] diff --git a/objc2-proc-macros/src/utils.rs b/objc2-proc-macros/src/utils.rs new file mode 100644 index 000000000..571392922 --- /dev/null +++ b/objc2-proc-macros/src/utils.rs @@ -0,0 +1,30 @@ +use syn::{Attribute, Ident, Meta, NestedMeta}; + +// Taken from `nom-derive`: +// https://github.com/rust-bakery/nom-derive/blob/5315891a0016b15094d4d0201f7d3ac803e4fc57/nom-derive-impl/src/enums.rs#L60-L90 +pub(crate) fn get_repr(attrs: &[Attribute]) -> Option { + for attr in attrs { + if let Ok(Meta::List(metalist)) = attr.parse_meta() { + if let Some(ident) = metalist.path.get_ident() { + if ident == "repr" { + for n in metalist.nested.iter() { + match n { + NestedMeta::Meta(meta) => match meta { + Meta::Path(path) => { + if let Some(word) = path.get_ident() { + return Some(word.clone()); + } else { + panic!("unsupported nested type for 'repr'") + } + } + _ => panic!("unsupported nested type for 'repr'"), + }, + _ => panic!("unsupported meta type for 'repr'"), + } + } + } + } + } + } + None +}