bean_macro/
lib.rs

1use proc_macro::TokenStream;
2use std::collections::HashSet;
3use std::hash::Hash;
4
5use proc_macro2::Span;
6use quote::quote;
7use syn::Type;
8use syn::braced;
9use syn::Ident;
10use syn::parse::{Parse, ParseStream};
11use syn::punctuated::Punctuated;
12use syn::Result;
13use syn::token::{Brace};
14use syn::Token;
15
16struct Syntax  {
17    ident: Ident,
18    _brace_token: Brace,
19    brace_fields: Punctuated<Type, Token![,]>,
20}
21
22struct Structure {
23    name: Ident,
24    tys: Vec<Type>
25}
26
27
28impl Parse for Structure {
29
30    fn parse(stream: ParseStream) -> Result<Self> {
31        let content;
32        let syntax = Syntax {
33            ident: stream.parse().unwrap(),
34            _brace_token: braced!(content in stream),
35            brace_fields: content.parse_terminated(Type::parse).unwrap()
36        };
37
38        let type_vec: Vec<Type> = syntax.brace_fields.into_iter().collect();
39
40        Ok(Structure {
41            name: syntax.ident,
42            tys: type_vec
43        })
44    }
45}
46
47fn has_unique_elements<T>(iter: T) -> bool
48    where
49        T: IntoIterator,
50        T::Item: Eq + Hash,
51{
52    let mut unique = HashSet::new();
53    iter.into_iter().all(move |x| unique.insert(x))
54}
55
56#[proc_macro]
57pub fn container(input: TokenStream) -> TokenStream {
58    let container_structure = syn::parse_macro_input!(input as Structure);
59    let container_name = container_structure.name;
60
61    if !has_unique_elements(container_structure.tys.clone()) {
62        panic!("duplicate type exist")
63    }
64
65    let field_types: Vec<Type> = container_structure.tys.clone();
66    let field_names: Vec<Ident> = field_types
67        .clone()
68        .into_iter()
69        .enumerate()
70        .map(|(i, _t)| Ident::new(format!("_com_{}_", i).as_str(), Span::call_site()))
71        .collect();
72
73    let struct_def = quote!{
74        #[derive(Default)]
75        pub struct #container_name {
76            #(#field_names: std::collections::HashMap<String, bean::component::Arc<#field_types>>),*
77        }
78    };
79
80    let comp_trait_impl = quote!{
81        #(
82            impl bean::component::HasComponent<#field_types> for #container_name {
83                fn put(&mut self, name: &str, component: bean::component::Arc<#field_types>) -> &mut Self{
84                    self.#field_names.insert(name.to_owned(), component);
85                    self
86                }
87
88                fn get(&self, name: &str) -> Option<bean::component::Arc<#field_types>> {
89                    self.#field_names.get(name).map(|c| c.clone())
90                }
91
92               fn get_all(&self) -> Vec<(&str, bean::component::Arc<#field_types>)> {
93                    self.#field_names.iter().map(|(k,v)| (k.as_str(), v.clone())).collect()
94                }
95            }
96        )*
97    };
98
99    let static_var = Ident::new(format!("__CONTAINER_{}__", container_name).as_str(), Span::call_site());
100
101    let container_def = quote!{
102        static mut #static_var: Option<#container_name> = Option::None;
103        impl #container_name {
104
105            fn init() -> &'static mut #container_name{
106                unsafe {
107                    #static_var = Some(#container_name::default());
108                    #static_var.as_mut().unwrap()
109                }
110            }
111
112            pub fn borrow() -> &'static #container_name {
113                unsafe { #static_var.as_ref().unwrap() }
114            }
115        }
116    };
117
118
119    let tokens = quote! {
120        #struct_def
121        #comp_trait_impl
122        #container_def
123    };
124
125    TokenStream::from(tokens)
126}