component-shape-gpui-macros 0.3.0

Proc macros for component-shape-gpui.
Documentation
use proc_macro2::TokenStream;
use quote::quote;
use syn::Expr;

pub fn constructor_body_tokens(new_expr: &Expr) -> TokenStream {
    if accepts_window_cx_arguments(new_expr) {
        quote! { (#new_expr)(window, cx) }
    } else {
        quote! { #new_expr }
    }
}

fn accepts_window_cx_arguments(expr: &Expr) -> bool {
    match expr {
        Expr::Group(group) => accepts_window_cx_arguments(&group.expr),
        Expr::Paren(paren) => accepts_window_cx_arguments(&paren.expr),
        Expr::Closure(_) | Expr::Path(_) => true,
        _ => false,
    }
}

#[cfg(test)]
mod tests {
    use quote::ToTokens as _;
    use syn::parse_quote;

    use super::constructor_body_tokens;

    fn compact(expr: syn::Expr) -> String {
        constructor_body_tokens(&expr)
            .to_token_stream()
            .to_string()
            .chars()
            .filter(|ch| !ch.is_whitespace())
            .collect()
    }

    #[test]
    fn constructor_paths_closures_and_wrappers_receive_gpui_context() {
        assert_eq!(compact(parse_quote!(State::new)), "(State::new)(window,cx)");
        assert_eq!(
            compact(parse_quote!((|window, cx| State::new(window, cx)))),
            "((|window,cx|State::new(window,cx)))(window,cx)"
        );
        let grouped = syn::Expr::Group(syn::ExprGroup {
            attrs: Vec::new(),
            group_token: Default::default(),
            expr: Box::new(parse_quote!(State::new)),
        });
        assert_eq!(compact(grouped), "(State::new)(window,cx)");
    }

    #[test]
    fn complete_constructor_expressions_are_emitted_unchanged() {
        assert_eq!(compact(parse_quote!(State::default())), "State::default()");
        assert_eq!(compact(parse_quote!(State { value: 1 })), "State{value:1}");
    }
}