use crate::prelude::NodeExpr;
use crate::tokenize::*;
use beet_core::prelude::*;
use beet_dom::prelude::*;
use proc_macro2::TokenStream;
use quote::quote;
use syn::LitStr;
pub fn tokenize_element_attributes(
world: &World,
entity_components: &mut Vec<TokenStream>,
entity: Entity,
) -> Result<()> {
let entity = world.entity(entity);
let Some(attrs) = entity.get::<Attributes>() else {
return Ok(());
};
let mut attr_entities = Vec::new();
for attr_entity in attrs.iter() {
let key = maybe_spanned_attr_key(world, attr_entity).map(
|(key_str, span)| {
let key_lit = LitStr::new(&key_str, span);
(key_str, key_lit)
},
);
let value = world.entity(attr_entity).get::<NodeExpr>().cloned();
let mut attr_components = Vec::new();
match (key, value) {
(Some((key_str, key)), Some(mut value)) => {
attr_components.push(quote! {AttributeKey::new(#key)});
if is_event(&key_str, &value) {
tokenize_event_handler(&key_str, key.span(), &mut value)?;
let parsed = value.bundle_tokens();
attr_components.push(quote! {
OnSpawnDeferred::insert_parent::<AttributeOf>(#parsed)
});
} else if key_str == "ref" {
let value = value.inner_parsed();
attr_components.push(quote! {
OnSpawnDeferred::parent::<AttributeOf>(move |entity|{
#value.set(entity.id());
Ok(())
})
});
} else {
attr_components.push(value.insert_deferred());
}
}
(Some((_, key)), None) => {
attr_components.push(quote! {AttributeKey::new(#key)});
}
(None, Some(value)) => {
entity_components.push(value.insert_deferred());
}
(None, None) => {}
}
if let Some(expr_idx) = world.entity(attr_entity).get::<ExprIdx>() {
attr_components.push(expr_idx.self_token_stream());
}
if attr_components.len() == 1 {
attr_entities.push(attr_components.pop().unwrap());
} else if !attr_components.is_empty() {
attr_entities.push(quote! {
(#(#attr_components),*)
});
}
}
if !attr_entities.is_empty() {
entity_components.push(quote! {
related!(Attributes[
#(#attr_entities),*
])
});
}
Ok(())
}
#[cfg(test)]
mod test {
use crate::prelude::*;
use beet_core::prelude::*;
use proc_macro2::TokenStream;
use quote::quote;
fn parse(tokens: TokenStream) -> TokenStream {
ParseRsxTokens::rstml_to_rsx(tokens, WsPathBuf::new(file!())).unwrap()
}
#[test]
fn key() {
quote! {
<span hidden/>
}
.xmap(parse)
.xpect_snapshot();
}
#[test]
fn key_value() {
quote! {
<span hidden=true/>
}
.xmap(parse)
.xpect_snapshot();
}
#[test]
fn block() {
quote! {
<span {foo}/>
}
.xmap(parse)
.xpect_snapshot();
}
#[test]
fn r#ref() {
quote! {
<span ref=span_ref/>
}
.xmap(parse)
.xpect_snapshot();
}
#[test]
fn events() {
quote! {
<span onclick={foo}/>
}
.xmap(parse)
.xpect_snapshot();
}
#[test]
fn js_events() {
quote! {
<span onclick="some_js_func"/>
}
.xmap(parse)
.xpect_snapshot();
}
#[cfg(feature = "css")]
#[test]
fn lang_src() {
quote! {
<style src="../../../../tests/test_site/components/style.css"/>
}
.xmap(parse)
.xpect_snapshot();
}
#[test]
fn all() {
quote! {
<span
hidden
class="foo"
{foo}
onmousemove="some_js_func"
onclick=|| { println!("clicked"); }
/>
}
.xmap(parse)
.xpect_snapshot();
}
}