Skip to content

Commit 1b4754c

Browse files
bors[bot]Bogay
andauthored
Merge #922
922: Some refactoring about `gdnative-derive` r=Bromeon a=Bogay - use `parse_quote!` instead of `quote!` to gain type inference. - add some methods to `PropertyAttrArgs` to has single point to define error so that they are not spreaded across `add_pair`. Co-authored-by: bogay <[email protected]>
2 parents 7650cad + 255ba90 commit 1b4754c

File tree

3 files changed

+89
-161
lines changed

3 files changed

+89
-161
lines changed

gdnative-derive/src/native_script.rs renamed to gdnative-derive/src/native_script/mod.rs

Lines changed: 16 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,7 @@ pub(crate) fn derive_native_class(derive_input: &DeriveInput) -> Result<TokenStr
6868
.default
6969
.map(|default_value| quote!(.with_default(#default_value)));
7070
let with_hint = config.hint.map(|hint_fn| quote!(.with_hint(#hint_fn())));
71-
let with_usage = if config.no_editor {
72-
Some(quote!(.with_usage(::gdnative::export::PropertyUsage::NOEDITOR)))
73-
} else {
74-
None
75-
};
71+
let with_usage = config.no_editor.then(|| quote!(.with_usage(::gdnative::export::PropertyUsage::NOEDITOR)));
7672
// check whether this property type is `Property<T>`. if so, extract T from it.
7773
let property_ty = match config.ty {
7874
Type::Path(ref path) => path
@@ -319,80 +315,61 @@ mod tests {
319315

320316
#[test]
321317
fn derive_property() {
322-
let input: TokenStream2 = syn::parse_str(
323-
r#"
318+
let input = parse_quote! {
324319
#[inherit(Node)]
325320
struct Foo {
326321
#[property]
327322
bar: String,
328-
}"#,
329-
)
330-
.unwrap();
331-
332-
let input: DeriveInput = syn::parse2(input).unwrap();
333-
323+
}
324+
};
334325
parse_derive_input(&input).unwrap();
335326
}
336327

337328
#[test]
338329
fn derive_property_no_editor() {
339-
let input: TokenStream2 = syn::parse_str(
340-
r#"
330+
let input = parse_quote! {
341331
#[inherit(Node)]
342332
struct Foo {
343333
#[property(no_editor)]
344334
bar: String,
345-
}"#,
346-
)
347-
.unwrap();
348-
349-
let input: DeriveInput = syn::parse2(input).unwrap();
350-
335+
}
336+
};
351337
parse_derive_input(&input).unwrap();
352338
}
353339

354340
#[test]
355341
fn derive_property_get_set() {
356-
let input: TokenStream2 = syn::parse_str(
357-
r#"
342+
let input = parse_quote! {
358343
#[inherit(Node)]
359344
struct Foo {
360345
#[property(get = "get_bar", set = "set_bar")]
361346
bar: i64,
362-
}"#,
363-
)
364-
.unwrap();
365-
let input: DeriveInput = syn::parse2(input).unwrap();
347+
}
348+
};
366349
parse_derive_input(&input).unwrap();
367350
}
368351

369352
#[test]
370353
fn derive_property_default_get_set() {
371-
let input: TokenStream2 = syn::parse_str(
372-
r#"
354+
let input = parse_quote! {
373355
#[inherit(Node)]
374356
struct Foo {
375357
#[property(get, set)]
376358
bar: i64,
377-
}"#,
378-
)
379-
.unwrap();
380-
let input: DeriveInput = syn::parse2(input).unwrap();
359+
}
360+
};
381361
parse_derive_input(&input).unwrap();
382362
}
383363

384364
#[test]
385365
fn derive_property_default_get_ref() {
386-
let input: TokenStream2 = syn::parse_str(
387-
r#"
366+
let input = parse_quote! {
388367
#[inherit(Node)]
389368
struct Foo {
390369
#[property(get_ref = "Self::get_bar")]
391370
bar: i64,
392-
}"#,
393-
)
394-
.unwrap();
395-
let input: DeriveInput = syn::parse2(input).unwrap();
371+
}
372+
};
396373
parse_derive_input(&input).unwrap();
397374
}
398375

gdnative-derive/src/native_script/property_args.rs

Lines changed: 73 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use proc_macro2::Span;
2+
use std::fmt::Debug;
13
use syn::spanned::Spanned;
24

35
#[derive(Debug)]
@@ -46,131 +48,86 @@ impl PropertyAttrArgsBuilder {
4648
}
4749
}
4850

51+
/// Error returned when a value are set twice
52+
/// e.g. #[property(set = "Self::set_foo", set = "Self::set_foo_again")]
53+
fn err_prop_already_set<T: Debug>(span: Span, prop: &str, old: &T) -> syn::Error {
54+
syn::Error::new(
55+
span,
56+
format!(
57+
"there is already a '{}' attribute with value: {:?}",
58+
prop, old,
59+
),
60+
)
61+
}
62+
63+
// Error returned when the attr value is not a string literal (i.e. not `LitStr`)
64+
fn err_attr_not_a_string_literal(span: Span, attr: &str) -> syn::Error {
65+
syn::Error::new(span, format!("'{}' value is not a string literal", attr))
66+
}
67+
68+
/// Convert `Lit` to `LitStr`
69+
fn extract_lit_str(lit: &syn::Lit) -> Option<&syn::LitStr> {
70+
if let syn::Lit::Str(lit_str) = lit {
71+
Some(lit_str)
72+
} else {
73+
None
74+
}
75+
}
76+
4977
pub fn add_pair(&mut self, pair: &syn::MetaNameValue) -> Result<(), syn::Error> {
50-
let path_span = pair.lit.span();
51-
let invalid_value_path = |_| {
52-
syn::Error::new(
53-
path_span,
54-
"Unexpected input, expected a double quoted string: \"path::to::something\"",
55-
)
78+
// Update property with input value.
79+
// Return error when there is already a value set
80+
macro_rules! update_prop {
81+
($prop:ident, $val:expr) => {
82+
if let Some(old) = self.$prop.replace($val) {
83+
return Err(Self::err_prop_already_set(
84+
pair.span(),
85+
stringify!($prop),
86+
&old,
87+
));
88+
}
89+
};
90+
}
91+
92+
// Convert input literal to a `syn::Path`
93+
let parse_path = |prop_name| {
94+
Self::extract_lit_str(&pair.lit)
95+
.ok_or_else(|| Self::err_attr_not_a_string_literal(pair.span(), prop_name))?
96+
.parse::<syn::Path>()
97+
.map_err(|_| {
98+
syn::Error::new(
99+
pair.lit.span(),
100+
"Unexpected input, expected a double quoted string: \"path::to::something\"",
101+
)
102+
})
56103
};
104+
// Convert input to `syn::Path`, and then update property
105+
macro_rules! process_path_input {
106+
($prop:ident$(, $($mapping:path),*)?) => {{
107+
let path = parse_path(stringify!($prop))?;
108+
$(
109+
$(let path = $mapping(path);)*
110+
)?
111+
update_prop!($prop, path);
112+
}};
113+
}
57114

58115
let name = pair
59116
.path
60117
.get_ident()
61118
.expect("should be single identifier")
62119
.to_string();
63120
match name.as_str() {
64-
"default" => {
65-
if let Some(old) = self.default.replace(pair.lit.clone()) {
66-
return Err(syn::Error::new(
67-
pair.span(),
68-
format!(
69-
"there is already a 'default' attribute with value: {:?}",
70-
old
71-
),
72-
));
73-
}
74-
}
121+
"default" => update_prop!(default, pair.lit.clone()),
75122
"path" => {
76-
let string = if let syn::Lit::Str(lit_str) = &pair.lit {
77-
lit_str.value()
78-
} else {
79-
return Err(syn::Error::new(
80-
pair.span(),
81-
"'path' value is not a string literal",
82-
));
83-
};
84-
85-
if let Some(old) = self.path.replace(string) {
86-
return Err(syn::Error::new(
87-
pair.span(),
88-
format!("there is already a 'path' attribute with value: {:?}", old),
89-
));
90-
}
91-
}
92-
"hint" => {
93-
let string = if let syn::Lit::Str(lit_str) = &pair.lit {
94-
lit_str.value()
95-
} else {
96-
return Err(syn::Error::new(
97-
pair.span(),
98-
"'hint' value is not a string literal",
99-
));
100-
};
101-
102-
let path =
103-
syn::parse_str::<syn::Path>(string.as_str()).map_err(invalid_value_path)?;
104-
if let Some(old) = self.hint.replace(path) {
105-
return Err(syn::Error::new(
106-
pair.span(),
107-
format!("there is already a 'hint' attribute with value: {:?}", old),
108-
));
109-
}
110-
}
111-
"get" => {
112-
let string = if let syn::Lit::Str(lit_str) = &pair.lit {
113-
lit_str.value()
114-
} else {
115-
return Err(syn::Error::new(
116-
pair.span(),
117-
"'get' value is not a string literal",
118-
));
119-
};
120-
121-
let path =
122-
syn::parse_str::<syn::Path>(string.as_str()).map_err(invalid_value_path)?;
123-
let get = PropertyGet::Owned(path);
124-
if let Some(old) = self.get.replace(get) {
125-
return Err(syn::Error::new(
126-
pair.span(),
127-
format!("there is already a 'get' attribute with value: {:?}", old),
128-
));
129-
}
130-
}
131-
"get_ref" => {
132-
let string = if let syn::Lit::Str(lit_str) = &pair.lit {
133-
lit_str.value()
134-
} else {
135-
return Err(syn::Error::new(
136-
pair.span(),
137-
"'get_ref' value is not a string literal",
138-
));
139-
};
140-
141-
let path =
142-
syn::parse_str::<syn::Path>(string.as_str()).map_err(invalid_value_path)?;
143-
let get_ref = PropertyGet::Ref(path);
144-
if let Some(old) = self.get.replace(get_ref) {
145-
return Err(syn::Error::new(
146-
pair.span(),
147-
format!(
148-
"there is already a 'get_ref' attribute with value: {:?}",
149-
old
150-
),
151-
));
152-
}
153-
}
154-
"set" => {
155-
let string = if let syn::Lit::Str(lit_str) = &pair.lit {
156-
lit_str.value()
157-
} else {
158-
return Err(syn::Error::new(
159-
pair.span(),
160-
"'set' value is not a string literal",
161-
));
162-
};
163-
164-
let path =
165-
syn::parse_str::<syn::Path>(string.as_str()).map_err(invalid_value_path)?;
166-
let set = PropertySet::WithPath(path);
167-
if let Some(old) = self.set.replace(set) {
168-
return Err(syn::Error::new(
169-
pair.span(),
170-
format!("there is already a 'set' attribute with value: {:?}", old),
171-
));
172-
}
123+
let path = Self::extract_lit_str(&pair.lit)
124+
.ok_or_else(|| Self::err_attr_not_a_string_literal(pair.span(), "path"))?;
125+
update_prop!(path, path.value());
173126
}
127+
"hint" => process_path_input!(hint),
128+
"get" => process_path_input!(get, PropertyGet::Owned),
129+
"get_ref" => process_path_input!(get, PropertyGet::Ref),
130+
"set" => process_path_input!(set, PropertySet::WithPath),
174131
_ => {
175132
return Err(syn::Error::new(
176133
pair.span(),
@@ -187,17 +144,11 @@ impl PropertyAttrArgsBuilder {
187144
self.no_editor = true;
188145
} else if path.is_ident("get") {
189146
if let Some(get) = self.get.replace(PropertyGet::Default) {
190-
return Err(syn::Error::new(
191-
path.span(),
192-
format!("there is already a 'get' attribute with value: {:?}", get),
193-
));
147+
return Err(Self::err_prop_already_set(path.span(), "get", &get));
194148
}
195149
} else if path.is_ident("set") {
196150
if let Some(set) = self.set.replace(PropertySet::Default) {
197-
return Err(syn::Error::new(
198-
path.span(),
199-
format!("there is already a 'set' attribute with value: {:?}", set),
200-
));
151+
return Err(Self::err_prop_already_set(path.span(), "set", &set));
201152
}
202153
} else {
203154
return Err(syn::Error::new(
File renamed without changes.

0 commit comments

Comments
 (0)