gd_resource_derive/
lib.rs

1extern crate proc_macro;
2use proc_macro::TokenStream;
3use quote::{ format_ident, quote };
4use syn;
5
6macro_rules! field_fmt {
7    () => {
8        "_{}_field"
9    };
10}
11
12#[proc_macro_derive(GDResource)]
13pub fn derive_resource_fn(_item: TokenStream) -> TokenStream {
14    let ast = syn::parse(_item).unwrap();
15
16    return impl_macro(&ast);
17}
18
19fn impl_macro(ast: &syn::DeriveInput) -> TokenStream {
20    let name = &ast.ident;
21    let data = &ast.data;
22
23    let syn::Data::Struct(data) = data else { unimplemented!() };
24
25    let fields_names = data.fields.iter().map(|field| {
26        let field_name = &field.ident.clone().unwrap();
27        let field_final = quote::format_ident!(field_fmt!(), field_name);
28
29        quote! {
30            #field_name: #field_final,
31        }
32    });
33
34    let fields_gd_impls = data.fields.iter().map(|field| {
35        let f_name = &field.ident.clone().unwrap();
36        let ty = &field.ty;
37
38        let ident_output = format_ident!(field_fmt!(), f_name);
39
40        quote! {
41            let _res = _object_repr.get(stringify!(#f_name));
42            let _res = <#ty as FromGodot>::try_from_variant(&_res);
43            let Some(#ident_output) = _res.ok() else {
44                return Err(godot::prelude::ConvertError::new(format!("{} convert error", stringify!(#f_name))));
45            };
46        }
47    });
48
49    let fields_resource_impls = data.fields.iter().map(|field| {
50        let f_name = &field.ident.clone().unwrap();
51        let ty = &field.ty;
52
53        let ident_output = format_ident!(field_fmt!(), f_name);
54
55        quote! {
56            let _res = self.get(stringify!(#f_name));
57            let _res = <#ty as FromGodot>::try_from_variant(&_res);
58            let Some(#ident_output) = _res.ok() else {
59                return #name::default();
60            };
61        }
62    });
63
64    let fields_names_2 = fields_names.clone();
65
66    let expanded =
67        quote! {
68        impl Into<#name> for godot::prelude::Gd<godot::prelude::Resource> {
69                fn into(self) -> #name {
70                    #(#fields_resource_impls)*
71                
72                    #name {
73                        #(#fields_names_2)*
74                    }
75                }
76        }
77
78        impl godot::prelude::GodotConvert for #name {
79            type Via = godot::prelude::Variant;
80        }
81
82        impl godot::prelude::FromGodot for #name {
83            fn try_from_godot (via: Self::Via) -> std::result::Result<Self, godot::prelude::ConvertError> {
84                let _object_repr: Gd<Object> = via.try_to()?;
85
86                #(#fields_gd_impls)*
87
88                Ok(Self {
89                    #(#fields_names)*
90                })
91            }
92        }
93    };
94
95    expanded.into()
96}