regecs_codegen/
lib.rs

1// Copyright (c) 2021, BlockProject 3D
2//
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without modification,
6// are permitted provided that the following conditions are met:
7//
8//     * Redistributions of source code must retain the above copyright notice,
9//       this list of conditions and the following disclaimer.
10//     * Redistributions in binary form must reproduce the above copyright notice,
11//       this list of conditions and the following disclaimer in the documentation
12//       and/or other materials provided with the distribution.
13//     * Neither the name of BlockProject 3D nor the names of its contributors
14//       may be used to endorse or promote products derived from this software
15//       without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29use std::vec::Vec;
30use std::string::String;
31use proc_macro::{self, TokenStream};
32use quote::quote;
33use syn::parse_macro_input;
34use syn::DeriveInput;
35use syn::FieldsNamed;
36use proc_macro2::Span;
37use syn::Fields;
38use syn::Data;
39use syn::Type;
40use syn::PathArguments;
41use syn::GenericArgument;
42use syn::Ident;
43use syn::Visibility;
44
45fn expand_type_name(t: &syn::Type) -> String
46{
47    let mut s = String::new();
48
49    match t
50    {
51        Type::Path(p) =>
52        {
53            for v in &p.path.segments
54            {
55                s.push_str(&v.ident.to_string());
56                s.push_str("::");
57            }
58            return String::from(&s[0..s.len() - 2]);
59        }
60        _ => panic!("Invalid generic type name for ComponentPool")
61    }
62}
63
64#[proc_macro_derive(ComponentManager)]
65pub fn component_manager(input: TokenStream) -> TokenStream
66{
67    let DeriveInput { ident, data, .. } = parse_macro_input!(input);
68    let mut v = Vec::new();
69
70    match data
71    {
72        Data::Struct(s) => match s.fields
73        {
74            Fields::Named(FieldsNamed { named, .. }) =>
75            {
76                for f in &named
77                {
78                    match &f.ty
79                    {
80                        Type::Path(p) =>
81                        {
82                            let last = &p.path.segments.last().unwrap();
83                            if let PathArguments::AngleBracketed(b) = &last.arguments
84                            {
85                                if let Some(v1) = b.args.first()
86                                {
87                                    if let GenericArgument::Type(t) = v1
88                                    {
89                                        let name = expand_type_name(&t);
90                                        if let Some(useless) = &f.ident
91                                        {
92                                            v.push((useless.clone(), name));
93                                        }
94                                        else
95                                        {
96                                            panic!("How is it possible that you get no identifier???!!!")
97                                        }
98                                    }
99                                    else
100                                    {
101                                        panic!("Could not identify type of component for field {:?}", f.ident);
102                                    }
103                                }
104                                else
105                                {
106                                    panic!("Could not identify type of component for field {:?}", f.ident);
107                                }
108                            }
109                        },
110                        _ => panic!("Could not identify type of component for field {:?}", f.ident),
111                    }
112                }
113            },
114            _ => panic!("Your component list must not be empty")
115        },
116        _ => panic!("ComponentManager cannot be implemented on non-structs")
117    };
118    let mut impl_base_tokens = Vec::new();
119    for (field_name, _) in &v
120    {
121        impl_base_tokens.push(
122            quote!
123            {
124                #field_name: ComponentPool::new()
125            }
126        );
127    };
128    let mut impls_tokens = Vec::new();
129    for (field_name, component_type) in &v
130    {
131        let mut s = component_type.clone();
132        s.push_str("Manager");
133        let new_ident = syn::parse_str::<Type>(&s).unwrap();
134        let new_ident1 = syn::parse_str::<Type>(&component_type).unwrap();
135        let mgr_impl_tokens = quote!
136        {
137            impl #new_ident for #ident
138            {
139                fn get(&mut self, id: usize) -> &mut #new_ident1
140                {
141                    return self.#field_name.get(id);
142                }
143
144                fn get_pool(&mut self) -> &mut ComponentPool<#new_ident1>
145                {
146                    return &mut self.#field_name;
147                }
148            }
149        };
150        impls_tokens.push(mgr_impl_tokens);
151    }
152    let output = quote!
153    {
154        impl #ident
155        {
156            pub fn new() -> #ident
157            {
158                return #ident
159                {
160                    #(#impl_base_tokens,)*
161                };
162            }
163        }
164
165        #(#impls_tokens)*
166    };
167    return output.into();
168}
169
170#[proc_macro_derive(Component)]
171pub fn component(input: TokenStream) -> TokenStream
172{
173    let DeriveInput { ident, vis, .. } = parse_macro_input!(input);
174
175    let mut s = ident.to_string();
176    s.push_str("Manager");
177    let new_ident = Ident::new(&s, Span::call_site());
178    let vis = match vis
179    {
180        Visibility::Public(_) => Some(Ident::new("pub", Span::call_site())),
181        _ => None
182    };
183    let output = quote!
184    {
185        #vis trait #new_ident
186        {
187            fn get(&mut self, id: usize) -> &mut #ident;
188            fn get_pool(&mut self) -> &mut ComponentPool<#ident>;
189        }
190    };
191    return output.into();
192}