Skip to content

Commit 3fb94a2

Browse files
committed
Derive stringly and numeric ToVariant for fieldless enums
Adds the `#[variant(enum = "repr")]` and `#[variant(enum = "str")]` item-level attribute to the Variant conversion derive macros for fieldless enums. - `#[variant(enum = "repr")]` - convert through the numeric type specified using `#[repr]`. - `#[variant(enum = "str")]` - convert through string representations of the variant names. Close #546.
1 parent 553bf87 commit 3fb94a2

19 files changed

+1114
-391
lines changed

gdnative-derive/src/native_script/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,14 +430,14 @@ mod tests {
430430
assert!(
431431
derived.is_ok(),
432432
"Valid derive expression fails to compile:\n{}",
433-
input().to_string()
433+
input()
434434
);
435435
} else {
436436
assert_eq!(
437437
derived.unwrap_err().to_string(),
438438
"The `#[property]` attribute requires explicit paths for `get` and `set` argument; \
439439
the defaults #[property], #[property(get)] and #[property(set)] are not allowed.",
440-
"Invalid derive expression compiles by mistake:\n{}", input().to_string()
440+
"Invalid derive expression compiles by mistake:\n{}", input()
441441
);
442442
}
443443
}

gdnative-derive/src/variant/attr.rs

Lines changed: 74 additions & 247 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,75 @@
1-
use std::iter::FromIterator;
2-
3-
use proc_macro2::Span;
4-
use syn::spanned::Spanned;
5-
6-
use super::Direction;
1+
macro_rules! impl_options {
2+
{
3+
self: $self:ident,
4+
match $ident:ident . as_str() {
5+
$( $name:ident, )*
6+
$( $alias:literal => $name_aliased:ident, )*
7+
}
8+
} => (
9+
match $ident.as_str() {
10+
$(
11+
stringify!($name) => {
12+
$self.$name = true;
13+
return Ok(());
14+
},
15+
)*
16+
$(
17+
$alias => {
18+
$self.$name_aliased = true;
19+
return Ok(());
20+
},
21+
)*
22+
_ => {},
23+
}
24+
);
25+
{
26+
self: $self:ident,
27+
match $ident:ident . as_str() = $lit:ident {
28+
$( $name:ident: $ty:ty, )*
29+
$( $alias:literal => $name_aliased:ident: $ty_aliased:ty, )*
30+
}
31+
} => (
32+
match $ident.as_str() {
33+
$(
34+
stringify!($name) => {
35+
let val = match $lit {
36+
syn::Lit::Str(lit_str) => lit_str.parse::<$ty>()?,
37+
_ => return Err(syn::Error::new($lit.span(), "expected string literal")),
38+
};
39+
40+
if $self.$name.replace(val).is_some() {
41+
return Err(syn::Error::new($lit.span(), format!(
42+
"the argument {} is already set",
43+
stringify!($name),
44+
)));
45+
}
746

8-
#[derive(Clone, Eq, PartialEq, Debug)]
9-
pub struct Attr {
10-
pub skip_to_variant: bool,
11-
pub skip_from_variant: bool,
12-
pub to_variant_with: Option<syn::Path>,
13-
pub from_variant_with: Option<syn::Path>,
14-
}
47+
return Ok(());
48+
},
49+
)*
50+
$(
51+
$alias => {
52+
let val = match $lit {
53+
syn::Lit::Str(lit_str) => lit_str.parse::<$ty_aliased>()?,
54+
_ => return Err(syn::Error::new($lit.span(), "expected string literal")),
55+
};
56+
57+
if $self.$name_aliased.replace(val).is_some() {
58+
return Err(syn::Error::new($lit.span(), format!(
59+
"the argument {} is already set",
60+
$alias,
61+
)));
62+
}
1563

16-
impl Attr {
17-
pub(crate) fn skip_bounds(&self, dir: Direction) -> bool {
18-
match dir {
19-
Direction::To => self.skip_to_variant,
20-
Direction::From => self.skip_from_variant,
64+
return Ok(());
65+
},
66+
)*
67+
_ => {},
2168
}
22-
}
23-
}
24-
25-
#[derive(Debug, Default)]
26-
pub struct AttrBuilder {
27-
skip_to_variant: bool,
28-
skip_from_variant: bool,
29-
to_variant_with: Option<syn::Path>,
30-
from_variant_with: Option<syn::Path>,
31-
errors: Vec<syn::Error>,
69+
)
3270
}
3371

34-
fn generate_error_with_docs(span: Span, message: &str) -> syn::Error {
72+
fn generate_error_with_docs(span: proc_macro2::Span, message: &str) -> syn::Error {
3573
syn::Error::new(
3674
span,
3775
format!(
@@ -41,224 +79,13 @@ fn generate_error_with_docs(span: Span, message: &str) -> syn::Error {
4179
)
4280
}
4381

44-
impl AttrBuilder {
45-
fn extend_meta(&mut self, meta: &syn::Meta) {
46-
match meta {
47-
syn::Meta::Path(flag) => self.set_flag(flag),
48-
syn::Meta::NameValue(pair) => self.set_pair(pair),
49-
syn::Meta::List(list) => {
50-
for nested in list.nested.iter() {
51-
match nested {
52-
syn::NestedMeta::Meta(meta) => self.extend_meta(meta),
53-
_ => {
54-
self.errors
55-
.push(syn::Error::new(nested.span(), "unexpected nested meta"));
56-
}
57-
}
58-
}
59-
}
60-
}
61-
}
62-
63-
fn set_flag(&mut self, flag: &syn::Path) {
64-
let err = self.try_set_flag(flag).err();
65-
self.errors.extend(err);
66-
}
67-
68-
fn try_set_flag(&mut self, flag: &syn::Path) -> Result<(), syn::Error> {
69-
let name = flag
70-
.get_ident()
71-
.ok_or_else(|| generate_error_with_docs(flag.span(), "Invalid syntax"))?
72-
.to_string();
73-
74-
macro_rules! impl_options {
75-
{
76-
match $ident:ident . as_str() {
77-
$( $name:ident, )*
78-
}
79-
} => (
80-
match $ident.as_str() {
81-
$(
82-
stringify!($name) => {
83-
self.$name = true;
84-
return Ok(());
85-
},
86-
)*
87-
_ => {},
88-
}
89-
)
90-
}
91-
92-
impl_options! {
93-
match name.as_str() {
94-
skip_to_variant,
95-
skip_from_variant,
96-
}
97-
}
98-
99-
#[allow(clippy::single_match)]
100-
match name.as_str() {
101-
"skip" => {
102-
self.skip_to_variant = true;
103-
self.skip_from_variant = true;
104-
return Ok(());
105-
}
106-
_ => {}
107-
}
108-
109-
Err(generate_error_with_docs(
110-
flag.span(),
111-
"Missing macro arguments",
112-
))
113-
}
114-
115-
fn set_pair(&mut self, pair: &syn::MetaNameValue) {
116-
let err = self.try_set_pair(pair).err();
117-
self.errors.extend(err);
118-
}
119-
120-
#[allow(clippy::single_match)]
121-
fn try_set_pair(&mut self, pair: &syn::MetaNameValue) -> Result<(), syn::Error> {
122-
let syn::MetaNameValue { path, lit, .. } = pair;
123-
124-
const VALID_KEYS: &str =
125-
"to_variant_with, from_variant_with, with, skip_to_variant, skip_from_variant, skip";
126-
127-
let name = path
128-
.get_ident()
129-
.ok_or_else(|| {
130-
let path_token = path.segments.iter().enumerate().fold(
131-
String::new(),
132-
|mut paths, (index, segment)| {
133-
if index > 0 {
134-
paths.push_str("::");
135-
}
136-
paths.push_str(&segment.ident.to_string());
137-
paths
138-
},
139-
);
140-
syn::Error::new(
141-
path.span(),
142-
&format!("Found {}, expected one of:\n\t{}", path_token, VALID_KEYS),
143-
)
144-
})?
145-
.to_string();
146-
147-
macro_rules! impl_options {
148-
{
149-
match $ident:ident . as_str() = $lit:ident {
150-
$( $name:ident: $ty:ty, )*
151-
}
152-
} => (
153-
match $ident.as_str() {
154-
$(
155-
stringify!($name) => {
156-
let val = match $lit {
157-
syn::Lit::Str(lit_str) => lit_str.parse::<$ty>()?,
158-
_ => return Err(syn::Error::new($lit.span(), "expected string literal")),
159-
};
160-
161-
if self.$name.replace(val).is_some() {
162-
return Err(syn::Error::new($lit.span(), format!(
163-
"the argument {} is already set",
164-
stringify!($name),
165-
)));
166-
}
167-
168-
return Ok(());
169-
},
170-
)*
171-
_ => {},
172-
}
173-
)
174-
}
175-
176-
impl_options! {
177-
match name.as_str() = lit {
178-
to_variant_with: syn::Path,
179-
from_variant_with: syn::Path,
180-
}
181-
}
182-
183-
match name.as_str() {
184-
"with" => {
185-
let path = match lit {
186-
syn::Lit::Str(lit_str) => lit_str.parse::<syn::Path>()?,
187-
_ => {
188-
return Err(syn::Error::new(
189-
lit.span(),
190-
"expecting a path to a module in double quotes: #[variant(with = \"path::to::mod\")]",
191-
))
192-
}
193-
};
194-
195-
if self
196-
.to_variant_with
197-
.replace(parse_quote!(#path::to_variant))
198-
.is_some()
199-
{
200-
return Err(syn::Error::new(
201-
lit.span(),
202-
"the argument to_variant_with is already set",
203-
));
204-
}
205-
206-
if self
207-
.from_variant_with
208-
.replace(parse_quote!(#path::from_variant))
209-
.is_some()
210-
{
211-
return Err(syn::Error::new(
212-
lit.span(),
213-
"the argument from_variant_with is already set",
214-
));
215-
}
216-
217-
return Ok(());
218-
}
219-
_ => {}
220-
}
221-
222-
Err(syn::Error::new(
223-
path.span(),
224-
format!("unknown argument, expected one of:\n\t{}", VALID_KEYS),
225-
))
226-
}
82+
pub trait AttrBuilder: FromIterator<syn::Meta> {
83+
type Attr;
84+
fn done(self) -> Result<Self::Attr, syn::Error>;
22785
}
22886

229-
impl FromIterator<syn::Meta> for AttrBuilder {
230-
fn from_iter<I>(iter: I) -> Self
231-
where
232-
I: IntoIterator<Item = syn::Meta>,
233-
{
234-
let mut builder = AttrBuilder::default();
235-
for meta in iter {
236-
builder.extend_meta(&meta);
237-
}
238-
builder
239-
}
240-
}
241-
242-
impl AttrBuilder {
243-
pub fn done(mut self) -> Result<Attr, syn::Error> {
244-
if self.errors.is_empty() {
245-
Ok(Attr {
246-
skip_to_variant: self.skip_to_variant,
247-
skip_from_variant: self.skip_from_variant,
248-
to_variant_with: self.to_variant_with,
249-
from_variant_with: self.from_variant_with,
250-
})
251-
} else {
252-
let first_error = self.errors.remove(0);
253-
let errors = self
254-
.errors
255-
.into_iter()
256-
.fold(first_error, |mut errors, error| {
257-
errors.combine(error);
258-
errors
259-
});
87+
pub mod field;
88+
pub mod item;
26089

261-
Err(errors)
262-
}
263-
}
264-
}
90+
pub use field::{FieldAttr, FieldAttrBuilder};
91+
pub use item::{ItemAttr, ItemAttrBuilder};

0 commit comments

Comments
 (0)