vertigo-macro 0.11.4

Reactive Real-DOM library with SSR for Rust - macros
Documentation
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use rstml::node::{KeyedAttributeValue, NodeName};
use std::collections::BTreeMap;
use syn::{Ident, spanned::Spanned};

use crate::component::get_group_attrs_push_method_name;

use super::commons::{
    dereference_maybe, generate_debug_class_name, is_default_block, take_block_or_literal_expr,
    unwrap_block_if_single,
};
use super::component::COMPONENT_ATTR_FORMAT_ERROR;

pub(super) fn convert_group_attrs(
    group: &str,
    attrs: &BTreeMap<String, KeyedAttributeValue>,
    constructor_name: &NodeName,
) -> TokenStream2 {
    let group = Ident::new(group, constructor_name.span());
    let group_method = get_group_attrs_push_method_name(&group);
    let mut attrs_stream = Vec::new();

    for (key, possible_value) in attrs {
        let matches = take_block_or_literal_expr(possible_value, COMPONENT_ATTR_FORMAT_ERROR);
        let key_value_pair = match matches {
            (Some(value), None) => {
                if is_default_block(&value.block) {
                    Some(quote! { #key.into(), vertigo::AttrGroupValue::AttrValue("".into()) })
                } else {
                    let value = dereference_maybe(&value.block)
                        .unwrap_or_else(|| unwrap_block_if_single(&value.block));
                    let variant = attr_to_group_variant(key, value);
                    Some(quote! { .#group_method(#key.into(), #variant) })
                }
            }
            (None, Some(lit)) => Some(quote! { .#group_method(#key.into(), #lit.into()) }),
            _ => None,
        };

        attrs_stream.push(key_value_pair);
    }

    quote! { #(#attrs_stream)* }
}

fn attr_to_group_variant(key: impl AsRef<str>, value: TokenStream2) -> TokenStream2 {
    match key.as_ref() {
        "css" => {
            let debug_class_name = generate_debug_class_name(&value);
            quote! { vertigo::AttrGroupValue::css(#value, #debug_class_name) }
        }
        "hook_key_down" => quote! { vertigo::AttrGroupValue::hook_key_down(#value) },
        "on_blur" => quote! { vertigo::AttrGroupValue::on_blur(#value) },
        "on_change" => quote! { vertigo::AttrGroupValue::on_change(#value) },
        "on_click" => quote! { vertigo::AttrGroupValue::on_click(#value) },
        "on_dropfile" => quote! { vertigo::AttrGroupValue::on_dropfile(#value) },
        "on_input" => quote! { vertigo::AttrGroupValue::on_input(#value) },
        "on_key_down" => quote! { vertigo::AttrGroupValue::on_key_down(#value) },
        "on_load" => quote! { vertigo::AttrGroupValue::on_load(#value) },
        "on_mouse_down" => quote! { vertigo::AttrGroupValue::on_mouse_down(#value) },
        "on_mouse_enter" => quote! { vertigo::AttrGroupValue::on_mouse_enter(#value) },
        "on_mouse_leave" => quote! { vertigo::AttrGroupValue::on_mouse_leave(#value) },
        "on_mouse_up" => quote! { vertigo::AttrGroupValue::on_mouse_up(#value) },
        "on_submit" | "form" => quote! { vertigo::AttrGroupValue::on_submit(#value) },
        _ => quote! { {#value}.into() },
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use quote::quote;

    fn tokens_eq(a: &TokenStream2, b: &TokenStream2) -> bool {
        a.to_string() == b.to_string()
    }

    #[test]
    fn test_attr_to_group_variant_css() {
        let val = quote! { my_css };
        let result = attr_to_group_variant("css", val);
        let s = result.to_string();
        assert!(
            s.contains("AttrGroupValue :: css"),
            "Expected css variant, got: {s}"
        );
        assert!(s.contains("my_css"), "Expected value token, got: {s}");
    }

    #[test]
    fn test_attr_to_group_variant_fallback() {
        let val = quote! { some_value };
        let result = attr_to_group_variant("unknown_attr", val.clone());
        let expected = quote! { { some_value }.into() };
        assert!(
            tokens_eq(&result, &expected),
            "Expected fallback into(), got: {}",
            result
        );
    }
}