compo_macros/
lib.rs

1mod arguments;
2mod block;
3#[macro_use]
4mod macros;
5mod utils;
6
7use {
8    arguments::handle_arguments,
9    block::handle_block,
10    proc_macro::{TokenStream, TokenTree},
11    utils::to_camel,
12};
13
14#[proc_macro_attribute]
15pub fn component(attrs: TokenStream, item: TokenStream) -> TokenStream {
16    let mut iter = item.into_iter();
17    let mut ident = iter.next();
18    let vis = if let Some(TokenTree::Ident(i)) = &ident
19        && i.to_string() == "pub"
20    {
21        ident = iter.next();
22        "pub"
23    } else {
24        ""
25    };
26    match &ident {
27        Some(TokenTree::Ident(i)) if i.to_string() != "async" => {
28            return error!(raw, i.span(), "Expected keyword `async`, got `{}`", i);
29        }
30        None => return error!(raw, "Expected keyword `async`, got eof"),
31        _ => (),
32    }
33    if let Some(TokenTree::Ident(i)) = iter.next()
34        && i.to_string() != "fn"
35    {
36        return error!(raw, i.span(), "Expected keyword `fn`, got `{}`", i);
37    }
38    let component_name = if let Some(TokenTree::Ident(i)) = iter.next() {
39        i.to_string()
40    } else {
41        return error!(raw, "Expected ident (component name), got eof");
42    };
43    let Some(TokenTree::Group(g)) = iter.next() else {
44        return error!(raw, "Expected function arguments (component properties)");
45    };
46    let (
47        component_arguments,
48        mut component_var_bindings,
49        property_field_defines,
50        property_field_initializers,
51        property_field_getters_and_setters,
52    ) = handle_arguments(g.stream(), &component_name);
53    let Some(TokenTree::Group(g)) = iter.next() else {
54        return error!(raw, "Expected function block");
55    };
56    let (component_block, mut field_defines, mut field_initializers) =
57        handle_block(g.stream(), &mut component_var_bindings, &component_name);
58    let component_name_camel = to_camel(&component_name);
59    field_defines.extend_from_slice(&property_field_defines);
60    let field_defines = field_defines
61        .iter()
62        .map(|i| i.to_string())
63        .collect::<String>();
64    let component_struct = ts!(
65        "{}\n{} struct {} <'a> {{\n_rt: Weak<Runtime<'a, ()>>,\n{}\n}}",
66        attrs,
67        vis,
68        component_name_camel,
69        field_defines
70    );
71    field_initializers.extend_from_slice(&property_field_initializers);
72    let field_initializers = field_initializers
73        .iter()
74        .map(|i| i.to_string())
75        .collect::<String>();
76
77    let component_new = ts!(
78        "fn new(rt: Weak<Runtime<'a, ()>>) -> Self {{ Self {{\n{}\n_rt: rt,\n}} }}",
79        field_initializers
80    );
81    let component_get_rt = ts!("fn get_rt(&self) -> Weak<Runtime<'a, ()>> {{ self._rt.clone() }}");
82
83    let component_field_getters_and_setters = property_field_getters_and_setters
84        .iter()
85        .map(|i| i.to_string())
86        .collect::<String>();
87    let component_impl = ts!(
88        "{}\nimpl<'a> Component<'a> for {} <'a> {{\n{}\n{}\n}}\n{}\nimpl<'a> {} <'a> {{\n{}\n}}",
89        attrs,
90        component_name_camel,
91        component_new,
92        component_get_rt,
93        attrs,
94        component_name_camel,
95        component_field_getters_and_setters
96    );
97    ts!(
98        "{}\n{}\n{}\n{} async fn {}({}) {}",
99        component_struct,
100        component_impl,
101        attrs,
102        vis,
103        component_name,
104        component_arguments,
105        component_block
106    )
107}