Skip to content

Commit

Permalink
feat(Export): add #[export(...)] attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
Bogay committed Oct 14, 2023
1 parent e055ea2 commit 76b7773
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 26 deletions.
107 changes: 93 additions & 14 deletions gdnative-derive/src/export.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,104 @@
use crate::crate_gdnative_core;
use proc_macro2::{Span, TokenStream as TokenStream2};
use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
use quote::ToTokens;
use syn::spanned::Spanned;
use syn::{DeriveInput, Fields};
use syn::{DeriveInput, Fields, Meta};

#[derive(Copy, Clone, Debug)]
enum Kind {
Enum,
}

#[derive(Debug)]
struct DeriveData {
kind: Kind,
ident: Ident,
data: syn::Data,
}

fn parse_derive_input(input: DeriveInput) -> syn::Result<DeriveData> {
let DeriveInput {
ident, data, attrs, ..
} = input.clone();

let (kind, errors) = attrs
.iter()
.fold((None, vec![]), |(mut kind, mut errors), attr| {
if attr.path.is_ident("export") {
if let Ok(Meta::List(list)) = attr.parse_meta() {
for meta in list.nested.into_iter() {
if let syn::NestedMeta::Meta(Meta::NameValue(pair)) = meta {
if !pair.path.is_ident("kind") {
errors.push(syn::Error::new(
pair.span(),
format!(
"Found {}, expected kind",
pair.path.into_token_stream()
),
))
} else if let syn::Lit::Str(str) = pair.lit {
if "enum" == str.value() {
if kind.is_some() {
errors
.push(syn::Error::new(str.span(), "kind already set"));
} else {
kind = Some(Kind::Enum);
}
} else {
errors.push(syn::Error::new(
str.span(),
format!("Found {}, expected enum", str.value()),
));
}
} else {
errors.push(syn::Error::new(
pair.lit.span(),
"Expected a string literal",
))
}
}
}
}
}

(kind, errors)
});

if let Some(err) = errors.into_iter().reduce(|mut acc, err| {
acc.combine(err);
acc
}) {
return Err(err);
}

match kind {
Some(kind) => Ok(DeriveData { ident, kind, data }),
None => Err(syn::Error::new(Span::call_site(), "kind not found")),
}
}

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

pub(crate) fn derive_export(input: &DeriveInput) -> syn::Result<TokenStream2> {
let derived_enum = match &input.data {
syn::Data::Enum(data) => data,
syn::Data::Struct(data) => {
return Err(err_only_supports_fieldless_enums(data.struct_token.span()));
}
syn::Data::Union(data) => {
return Err(err_only_supports_fieldless_enums(data.union_token.span()));
}
};
pub(crate) fn derive_export(input: DeriveInput) -> syn::Result<TokenStream2> {
let derive_data = parse_derive_input(input)?;

let export_impl = impl_export(&input.ident, derived_enum)?;
Ok(export_impl)
match derive_data.kind {
Kind::Enum => {
let derived_enum = match derive_data.data {
syn::Data::Enum(data) => data,
syn::Data::Struct(data) => {
return Err(err_only_supports_fieldless_enums(data.struct_token.span()));
}
syn::Data::Union(data) => {
return Err(err_only_supports_fieldless_enums(data.union_token.span()));
}
};
let export_impl = impl_export(&derive_data.ident, &derived_enum)?;
Ok(export_impl)
}
}
}

fn impl_export(enum_ty: &syn::Ident, data: &syn::DataEnum) -> syn::Result<TokenStream2> {
Expand Down
5 changes: 3 additions & 2 deletions gdnative-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,7 @@ pub fn godot_wrap_method(input: TokenStream) -> TokenStream {
///
/// #[derive(Debug, PartialEq, Clone, Copy, Export, ToVariant, FromVariant)]
/// #[variant(enum = "repr")]
/// #[export(kind = "enum")]
/// #[repr(i32)]
/// enum Dir {
/// Up = 1,
Expand Down Expand Up @@ -712,10 +713,10 @@ pub fn godot_wrap_method(input: TokenStream) -> TokenStream {
/// f1: i32
/// }
/// ```
#[proc_macro_derive(Export)]
#[proc_macro_derive(Export, attributes(export))]
pub fn derive_export(input: TokenStream) -> TokenStream {
let derive_input = syn::parse_macro_input!(input as syn::DeriveInput);
match export::derive_export(&derive_input) {
match export::derive_export(derive_input) {
Ok(stream) => stream.into(),
Err(err) => err.to_compile_error().into(),
}
Expand Down
1 change: 1 addition & 0 deletions gdnative/tests/ui/export_fail_01.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use gdnative::prelude::*;

#[derive(Export, ToVariant)]
#[export(kind = "enum")]
pub enum Foo {
Bar(String),
Baz { a: i32, b: u32 },
Expand Down
8 changes: 4 additions & 4 deletions gdnative/tests/ui/export_fail_01.stderr
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
error: #[derive(Export)] only supports fieldless enums
--> tests/ui/export_fail_01.rs:5:5
--> tests/ui/export_fail_01.rs:6:5
|
5 | Bar(String),
6 | Bar(String),
| ^^^

error: #[derive(Export)] only supports fieldless enums
--> tests/ui/export_fail_01.rs:6:5
--> tests/ui/export_fail_01.rs:7:5
|
6 | Baz { a: i32, b: u32 },
7 | Baz { a: i32, b: u32 },
| ^^^
1 change: 1 addition & 0 deletions gdnative/tests/ui/export_fail_02.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use gdnative::prelude::*;

#[derive(Export, ToVariant)]
#[export(kind = "enum")]
pub struct Foo {
bar: i32,
}
Expand Down
4 changes: 2 additions & 2 deletions gdnative/tests/ui/export_fail_02.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: #[derive(Export)] only supports fieldless enums
--> tests/ui/export_fail_02.rs:4:5
--> tests/ui/export_fail_02.rs:5:5
|
4 | pub struct Foo {
5 | pub struct Foo {
| ^^^^^^
1 change: 1 addition & 0 deletions gdnative/tests/ui/export_fail_03.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use gdnative::prelude::*;

#[derive(Export, ToVariant)]
#[export(kind = "enum")]
pub union Foo {
bar: i32,
}
Expand Down
8 changes: 4 additions & 4 deletions gdnative/tests/ui/export_fail_03.stderr
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
error: #[derive(Export)] only supports fieldless enums
--> tests/ui/export_fail_03.rs:4:5
--> tests/ui/export_fail_03.rs:5:5
|
4 | pub union Foo {
5 | pub union Foo {
| ^^^^^

error: Variant conversion derive macro does not work on unions.
--> tests/ui/export_fail_03.rs:4:1
|
4 | pub union Foo {
| ^^^
4 | #[export(kind = "enum")]
| ^
1 change: 1 addition & 0 deletions gdnative/tests/ui/export_pass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use gdnative::prelude::*;

#[derive(Export, ToVariant, Clone, Copy)]
#[variant(enum = "repr")]
#[export(kind = "enum")]
#[repr(i32)]
pub enum Foo {
Bar,
Expand Down

0 comments on commit 76b7773

Please sign in to comment.