Skip to content

Commit 76b7773

Browse files
committed
feat(Export): add #[export(...)] attribute
1 parent e055ea2 commit 76b7773

9 files changed

+110
-26
lines changed

gdnative-derive/src/export.rs

+93-14
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,104 @@
11
use crate::crate_gdnative_core;
2-
use proc_macro2::{Span, TokenStream as TokenStream2};
2+
use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
3+
use quote::ToTokens;
34
use syn::spanned::Spanned;
4-
use syn::{DeriveInput, Fields};
5+
use syn::{DeriveInput, Fields, Meta};
6+
7+
#[derive(Copy, Clone, Debug)]
8+
enum Kind {
9+
Enum,
10+
}
11+
12+
#[derive(Debug)]
13+
struct DeriveData {
14+
kind: Kind,
15+
ident: Ident,
16+
data: syn::Data,
17+
}
18+
19+
fn parse_derive_input(input: DeriveInput) -> syn::Result<DeriveData> {
20+
let DeriveInput {
21+
ident, data, attrs, ..
22+
} = input.clone();
23+
24+
let (kind, errors) = attrs
25+
.iter()
26+
.fold((None, vec![]), |(mut kind, mut errors), attr| {
27+
if attr.path.is_ident("export") {
28+
if let Ok(Meta::List(list)) = attr.parse_meta() {
29+
for meta in list.nested.into_iter() {
30+
if let syn::NestedMeta::Meta(Meta::NameValue(pair)) = meta {
31+
if !pair.path.is_ident("kind") {
32+
errors.push(syn::Error::new(
33+
pair.span(),
34+
format!(
35+
"Found {}, expected kind",
36+
pair.path.into_token_stream()
37+
),
38+
))
39+
} else if let syn::Lit::Str(str) = pair.lit {
40+
if "enum" == str.value() {
41+
if kind.is_some() {
42+
errors
43+
.push(syn::Error::new(str.span(), "kind already set"));
44+
} else {
45+
kind = Some(Kind::Enum);
46+
}
47+
} else {
48+
errors.push(syn::Error::new(
49+
str.span(),
50+
format!("Found {}, expected enum", str.value()),
51+
));
52+
}
53+
} else {
54+
errors.push(syn::Error::new(
55+
pair.lit.span(),
56+
"Expected a string literal",
57+
))
58+
}
59+
}
60+
}
61+
}
62+
}
63+
64+
(kind, errors)
65+
});
66+
67+
if let Some(err) = errors.into_iter().reduce(|mut acc, err| {
68+
acc.combine(err);
69+
acc
70+
}) {
71+
return Err(err);
72+
}
73+
74+
match kind {
75+
Some(kind) => Ok(DeriveData { ident, kind, data }),
76+
None => Err(syn::Error::new(Span::call_site(), "kind not found")),
77+
}
78+
}
579

680
fn err_only_supports_fieldless_enums(span: Span) -> syn::Error {
781
syn::Error::new(span, "#[derive(Export)] only supports fieldless enums")
882
}
983

10-
pub(crate) fn derive_export(input: &DeriveInput) -> syn::Result<TokenStream2> {
11-
let derived_enum = match &input.data {
12-
syn::Data::Enum(data) => data,
13-
syn::Data::Struct(data) => {
14-
return Err(err_only_supports_fieldless_enums(data.struct_token.span()));
15-
}
16-
syn::Data::Union(data) => {
17-
return Err(err_only_supports_fieldless_enums(data.union_token.span()));
18-
}
19-
};
84+
pub(crate) fn derive_export(input: DeriveInput) -> syn::Result<TokenStream2> {
85+
let derive_data = parse_derive_input(input)?;
2086

21-
let export_impl = impl_export(&input.ident, derived_enum)?;
22-
Ok(export_impl)
87+
match derive_data.kind {
88+
Kind::Enum => {
89+
let derived_enum = match derive_data.data {
90+
syn::Data::Enum(data) => data,
91+
syn::Data::Struct(data) => {
92+
return Err(err_only_supports_fieldless_enums(data.struct_token.span()));
93+
}
94+
syn::Data::Union(data) => {
95+
return Err(err_only_supports_fieldless_enums(data.union_token.span()));
96+
}
97+
};
98+
let export_impl = impl_export(&derive_data.ident, &derived_enum)?;
99+
Ok(export_impl)
100+
}
101+
}
23102
}
24103

25104
fn impl_export(enum_ty: &syn::Ident, data: &syn::DataEnum) -> syn::Result<TokenStream2> {

gdnative-derive/src/lib.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,7 @@ pub fn godot_wrap_method(input: TokenStream) -> TokenStream {
676676
///
677677
/// #[derive(Debug, PartialEq, Clone, Copy, Export, ToVariant, FromVariant)]
678678
/// #[variant(enum = "repr")]
679+
/// #[export(kind = "enum")]
679680
/// #[repr(i32)]
680681
/// enum Dir {
681682
/// Up = 1,
@@ -712,10 +713,10 @@ pub fn godot_wrap_method(input: TokenStream) -> TokenStream {
712713
/// f1: i32
713714
/// }
714715
/// ```
715-
#[proc_macro_derive(Export)]
716+
#[proc_macro_derive(Export, attributes(export))]
716717
pub fn derive_export(input: TokenStream) -> TokenStream {
717718
let derive_input = syn::parse_macro_input!(input as syn::DeriveInput);
718-
match export::derive_export(&derive_input) {
719+
match export::derive_export(derive_input) {
719720
Ok(stream) => stream.into(),
720721
Err(err) => err.to_compile_error().into(),
721722
}

gdnative/tests/ui/export_fail_01.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use gdnative::prelude::*;
22

33
#[derive(Export, ToVariant)]
4+
#[export(kind = "enum")]
45
pub enum Foo {
56
Bar(String),
67
Baz { a: i32, b: u32 },
+4-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
error: #[derive(Export)] only supports fieldless enums
2-
--> tests/ui/export_fail_01.rs:5:5
2+
--> tests/ui/export_fail_01.rs:6:5
33
|
4-
5 | Bar(String),
4+
6 | Bar(String),
55
| ^^^
66

77
error: #[derive(Export)] only supports fieldless enums
8-
--> tests/ui/export_fail_01.rs:6:5
8+
--> tests/ui/export_fail_01.rs:7:5
99
|
10-
6 | Baz { a: i32, b: u32 },
10+
7 | Baz { a: i32, b: u32 },
1111
| ^^^

gdnative/tests/ui/export_fail_02.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use gdnative::prelude::*;
22

33
#[derive(Export, ToVariant)]
4+
#[export(kind = "enum")]
45
pub struct Foo {
56
bar: i32,
67
}
+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: #[derive(Export)] only supports fieldless enums
2-
--> tests/ui/export_fail_02.rs:4:5
2+
--> tests/ui/export_fail_02.rs:5:5
33
|
4-
4 | pub struct Foo {
4+
5 | pub struct Foo {
55
| ^^^^^^

gdnative/tests/ui/export_fail_03.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use gdnative::prelude::*;
22

33
#[derive(Export, ToVariant)]
4+
#[export(kind = "enum")]
45
pub union Foo {
56
bar: i32,
67
}
+4-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
error: #[derive(Export)] only supports fieldless enums
2-
--> tests/ui/export_fail_03.rs:4:5
2+
--> tests/ui/export_fail_03.rs:5:5
33
|
4-
4 | pub union Foo {
4+
5 | pub union Foo {
55
| ^^^^^
66

77
error: Variant conversion derive macro does not work on unions.
88
--> tests/ui/export_fail_03.rs:4:1
99
|
10-
4 | pub union Foo {
11-
| ^^^
10+
4 | #[export(kind = "enum")]
11+
| ^

gdnative/tests/ui/export_pass.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use gdnative::prelude::*;
22

33
#[derive(Export, ToVariant, Clone, Copy)]
44
#[variant(enum = "repr")]
5+
#[export(kind = "enum")]
56
#[repr(i32)]
67
pub enum Foo {
78
Bar,

0 commit comments

Comments
 (0)