compo-macros 0.1.3

Compo is a general-purpose declarative and reactive component framework designed for single-threaded asynchronous execution, offering high performance and safety guarantees. Suitable for GUI scenarios or other similar non-GUI component systems.
Documentation
mod arguments;
mod block;
#[macro_use]
mod macros;
mod utils;

use {
    arguments::handle_arguments,
    block::handle_block,
    proc_macro::{TokenStream, TokenTree},
    utils::to_camel,
};

#[proc_macro_attribute]
pub fn component(attrs: TokenStream, item: TokenStream) -> TokenStream {
    let mut iter = item.into_iter();
    let mut ident = iter.next();
    let vis = if let Some(TokenTree::Ident(i)) = &ident
        && i.to_string() == "pub"
    {
        ident = iter.next();
        "pub"
    } else {
        ""
    };
    match &ident {
        Some(TokenTree::Ident(i)) if i.to_string() != "async" => {
            return error!(raw, i.span(), "Expected keyword `async`, got `{}`", i);
        }
        None => return error!(raw, "Expected keyword `async`, got eof"),
        _ => (),
    }
    if let Some(TokenTree::Ident(i)) = iter.next()
        && i.to_string() != "fn"
    {
        return error!(raw, i.span(), "Expected keyword `fn`, got `{}`", i);
    }
    let component_name = if let Some(TokenTree::Ident(i)) = iter.next() {
        i.to_string()
    } else {
        return error!(raw, "Expected ident (component name), got eof");
    };
    let Some(TokenTree::Group(g)) = iter.next() else {
        return error!(raw, "Expected function arguments (component properties)");
    };
    let (
        component_arguments,
        mut component_var_bindings,
        property_field_defines,
        property_field_initializers,
        property_field_getters_and_setters,
    ) = handle_arguments(g.stream(), &component_name);
    let Some(TokenTree::Group(g)) = iter.next() else {
        return error!(raw, "Expected function block");
    };
    let (component_block, mut field_defines, mut field_initializers) =
        handle_block(g.stream(), &mut component_var_bindings, &component_name);
    let component_name_camel = to_camel(&component_name);
    field_defines.extend_from_slice(&property_field_defines);
    let field_defines = field_defines
        .iter()
        .map(|i| i.to_string())
        .collect::<String>();
    let component_struct = ts!(
        "{}\n{} struct {} <'a> {{\n_rt: Weak<Runtime<'a, ()>>,\n_cancellable: RefCell<Cancellable>,\n{}\n}}",
        attrs,
        vis,
        component_name_camel,
        field_defines
    );
    field_initializers.extend_from_slice(&property_field_initializers);
    let field_initializers = field_initializers
        .iter()
        .map(|i| i.to_string())
        .collect::<String>();

    let component_new = ts!(
        "fn new(rt: Weak<Runtime<'a, ()>>) -> Self {{ Self {{\n{}\n_rt: rt,\n_cancellable: Default::default(),\n}} }}",
        field_initializers
    );
    let component_get_rt = ts!("fn get_rt(&self) -> Weak<Runtime<'a, ()>> {{ self._rt.clone() }}");
    let component_update = ts!(
        "fn update(self: &Rc<Self>) {{\nlet mut cancellable = self._cancellable.borrow_mut();\ncancellable.cancel();\n*cancellable = self.spawn({}(Rc::downgrade(self)));\n}}",
        component_name
    );

    let component_field_getters_and_setters = property_field_getters_and_setters
        .iter()
        .map(|i| i.to_string())
        .collect::<String>();
    let component_impl = ts!(
        "{}\nimpl<'a> Component<'a> for {} <'a> {{\n{}\n{}\n{}\n}}\n{}\nimpl<'a> {} <'a> {{\n{}\n}}",
        attrs,
        component_name_camel,
        component_new,
        component_get_rt,
        component_update,
        attrs,
        component_name_camel,
        component_field_getters_and_setters
    );
    ts!(
        "{}\n{}\n{}\n{} async fn {}({}) {}",
        component_struct,
        component_impl,
        attrs,
        vis,
        component_name,
        component_arguments,
        component_block
    )
}