Skip to content

Commit 6f32a28

Browse files
committed
Add initial derive(Encode, RefEncode) implementation
1 parent 1b3c9e9 commit 6f32a28

File tree

4 files changed

+121
-11
lines changed

4 files changed

+121
-11
lines changed

objc2-encode/tests/test_derive.rs

+25
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,28 @@ fn cgpoint() {
2020
assert_eq!(CGPoint::ENCODING, enc);
2121
assert_eq!(CGPoint::ENCODING_REF, Encoding::Pointer(&enc));
2222
}
23+
24+
#[derive(Encode, RefEncode)]
25+
#[repr(transparent)]
26+
struct Transparent {
27+
_inner: usize,
28+
}
29+
30+
#[test]
31+
fn transparent() {
32+
assert_eq!(Transparent::ENCODING, usize::ENCODING);
33+
assert_eq!(Transparent::ENCODING_REF, usize::ENCODING_REF);
34+
}
35+
36+
#[derive(Encode, RefEncode)]
37+
#[repr(usize)]
38+
enum MyEnum {
39+
_A = 1,
40+
_B = 2,
41+
}
42+
43+
#[test]
44+
fn enum_repr() {
45+
assert_eq!(MyEnum::ENCODING, usize::ENCODING);
46+
assert_eq!(MyEnum::ENCODING_REF, usize::ENCODING_REF);
47+
}

objc2-proc-macros/src/derive.rs

+65-11
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,78 @@
11
use proc_macro::TokenStream;
22
use quote::quote;
3-
use syn::DeriveInput;
3+
use syn::{Data, DeriveInput};
4+
5+
use crate::utils::get_repr;
46

57
pub(crate) fn impl_encode(ast: &DeriveInput) -> TokenStream {
6-
let name = &ast.ident;
7-
let gen = quote! {
8-
unsafe impl ::objc2_encode::Encode for #name {
9-
const ENCODING: ::objc2_encode::Encoding<'static> = ::objc2_encode::Encoding::Struct(
10-
stringify!(#name),
11-
&[CGFloat::ENCODING, CGFloat::ENCODING],
12-
);
8+
let DeriveInput {
9+
ident, data, attrs, ..
10+
} = ast;
11+
12+
let repr = match get_repr(&attrs) {
13+
Some(repr) => repr,
14+
None => panic!("Missing repr"),
15+
};
16+
17+
let encoding = match data {
18+
Data::Struct(data) => {
19+
let fields = data
20+
.fields
21+
.iter()
22+
.map(|field| &field.ty)
23+
.collect::<Vec<_>>();
24+
match &*repr.to_string() {
25+
"transparent" => {
26+
let field = fields[0];
27+
assert_eq!(fields.len(), 1, "Expected one item");
28+
quote!(<#field as ::objc2_encode::Encode>::ENCODING)
29+
}
30+
"C" => {
31+
quote!(
32+
::objc2_encode::Encoding::Struct(
33+
stringify!(#ident),
34+
&[#(<#fields as ::objc2_encode::Encode>::ENCODING),*],
35+
)
36+
)
37+
}
38+
_ => panic!("Unknown repr"),
39+
}
1340
}
41+
Data::Enum(_) => {
42+
let ty = match &*repr.to_string() {
43+
"usize" => quote! { core::primitive::usize },
44+
"isize" => quote! { core::primitive::isize },
45+
"u64" => quote! { core::primitive::u64 },
46+
"i64" => quote! { core::primitive::i64 },
47+
"u32" => quote! { core::primitive::u32 },
48+
"i32" => quote! { core::primitive::i32 },
49+
"u16" => quote! { core::primitive::u16 },
50+
"i16" => quote! { core::primitive::i16 },
51+
"u8" => quote! { core::primitive::u8 },
52+
"i8" => quote! { core::primitive::i8 },
53+
_ => panic!("Unknown repr"),
54+
};
55+
quote! { <#ty as ::objc2_encode::Encode>::ENCODING }
56+
}
57+
Data::Union(_) => unimplemented!(),
1458
};
15-
gen.into()
59+
60+
// TODO: Generics
61+
quote! {
62+
unsafe impl ::objc2_encode::Encode for #ident {
63+
const ENCODING: ::objc2_encode::Encoding<'static> = #encoding;
64+
}
65+
}
66+
.into()
1667
}
1768

1869
pub(crate) fn impl_ref_encode(ast: &DeriveInput) -> TokenStream {
19-
let name = &ast.ident;
70+
let DeriveInput { ident, .. } = ast;
71+
// TODO: Generics
72+
// TODO: Objects
73+
2074
let gen = quote! {
21-
unsafe impl ::objc2_encode::RefEncode for #name {
75+
unsafe impl ::objc2_encode::RefEncode for #ident {
2276
const ENCODING_REF: ::objc2_encode::Encoding<'static> = ::objc2_encode::Encoding::Pointer(
2377
&<Self as ::objc2_encode::Encode>::ENCODING
2478
);

objc2-proc-macros/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ extern "C" {}
2020
use proc_macro::TokenStream;
2121

2222
mod derive;
23+
mod utils;
2324

2425
/// TODO
2526
#[proc_macro_derive(Encode)]

objc2-proc-macros/src/utils.rs

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use syn::{Attribute, Ident, Meta, NestedMeta};
2+
3+
// Taken from `nom-derive`:
4+
// https://github.com/rust-bakery/nom-derive/blob/5315891a0016b15094d4d0201f7d3ac803e4fc57/nom-derive-impl/src/enums.rs#L60-L90
5+
pub(crate) fn get_repr(attrs: &[Attribute]) -> Option<Ident> {
6+
for attr in attrs {
7+
if let Ok(Meta::List(metalist)) = attr.parse_meta() {
8+
if let Some(ident) = metalist.path.get_ident() {
9+
if ident == "repr" {
10+
for n in metalist.nested.iter() {
11+
match n {
12+
NestedMeta::Meta(meta) => match meta {
13+
Meta::Path(path) => {
14+
if let Some(word) = path.get_ident() {
15+
return Some(word.clone());
16+
} else {
17+
panic!("unsupported nested type for 'repr'")
18+
}
19+
}
20+
_ => panic!("unsupported nested type for 'repr'"),
21+
},
22+
_ => panic!("unsupported meta type for 'repr'"),
23+
}
24+
}
25+
}
26+
}
27+
}
28+
}
29+
None
30+
}

0 commit comments

Comments
 (0)