use crate::child::Child;
use crate::children::Children;
use crate::element_attribute::ElementAttribute;
use crate::element_attributes::ElementAttributes;
use crate::tags::{ClosingTag, OpenTag};
use quote::{quote, ToTokens};
use syn::parse::{Parse, ParseStream, Result};
pub struct Element {
name: syn::Path,
attributes: ElementAttributes,
children: Children,
}
impl Parse for Element {
fn parse(input: ParseStream) -> Result<Self> {
let open_tag = input.parse::<OpenTag>()?;
let children = if open_tag.self_closing {
Children::default()
} else {
let children = input.parse::<Children>()?;
let closing_tag = input.parse::<ClosingTag>()?;
closing_tag.validate(&open_tag);
children
};
Ok(Element {
name: open_tag.name,
attributes: open_tag.attributes,
children,
})
}
}
impl Element {
pub fn is_custom_element(&self) -> bool {
match self.name.get_ident() {
None => true,
Some(ident) => {
let name = ident.to_string();
let first_letter = name.get(0..1).unwrap();
first_letter.to_uppercase() == first_letter
}
}
}
pub fn collect_class_names(&self) -> Vec<String> {
let mut classes = Vec::new();
for attr in &self.attributes.attributes {
if let ElementAttribute::WithValue(key, block) = attr {
let key_str = key
.iter()
.map(|i| i.to_string())
.collect::<Vec<_>>()
.join("-");
if key_str == "class" {
if block.stmts.len() == 1 {
if let syn::Stmt::Expr(syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Str(s),
..
})) = &block.stmts[0]
{
for class_name in s.value().split_whitespace() {
classes.push(class_name.to_string());
}
}
}
}
}
}
for child in &self.children.nodes {
Child::collect_class_names_from(child, &mut classes);
}
classes
}
pub fn inject_tailwind_style(&mut self, class_names: Vec<String>) -> bool {
if let Some(ident) = self.name.get_ident() {
if ident.to_string() == "head" {
self.children
.nodes
.push(Child::TailwindStyle(class_names));
return true;
}
}
for child in &mut self.children.nodes {
if let Child::Element(el) = child {
if el.inject_tailwind_style(class_names.clone()) {
return true;
}
}
}
false
}
pub fn prepend_child(&mut self, child: Child) {
self.children.nodes.insert(0, child);
}
}
impl ToTokens for Element {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let name = &self.name;
let declaration = if self.is_custom_element() {
let attrs = self.attributes.for_custom_element(&self.children);
quote! { #name #attrs }
} else {
let attrs = self.attributes.for_simple_element();
let children_tuple = self.children.as_option_of_tuples_tokens();
quote! {
workers_rsx::SimpleElement {
tag_name: stringify!(#name),
attributes: #attrs,
contents: #children_tuple,
}
}
};
declaration.to_tokens(tokens);
}
}