~repos /tide-jsx

#rust#proc-macro#jsx

git clone https://pyrossh.dev/repos/tide-jsx.git

Tide + JSX



file:

impl/src/element_attributes.rs



use crate::children::Children;
use crate::element_attribute::ElementAttribute;
use proc_macro_error::emit_error;
use quote::{quote, ToTokens};
use std::collections::HashSet;
use syn::ext::IdentExt;
use syn::parse::{Parse, ParseStream, Result};
use syn::spanned::Spanned;
pub type Attributes = HashSet<ElementAttribute>;
#[derive(Default)]
pub struct ElementAttributes {
pub attributes: Attributes,
}
impl ElementAttributes {
pub fn new(attributes: Attributes) -> Self {
Self { attributes }
}
pub fn for_custom_element<'c>(
&self,
children: &'c Children,
) -> CustomElementAttributes<'_, 'c> {
CustomElementAttributes {
attributes: &self.attributes,
children,
}
}
pub fn for_simple_element(&self) -> SimpleElementAttributes<'_> {
SimpleElementAttributes {
attributes: &self.attributes,
}
}
pub fn parse(input: ParseStream, is_custom_element: bool) -> Result<Self> {
let mut parsed_self = input.parse::<Self>()?;
let new_attributes: Attributes = parsed_self
.attributes
.drain()
.filter_map(|attribute| match attribute.validate(is_custom_element) {
Ok(x) => Some(x),
Err(err) => {
emit_error!(err.span(), "Invalid attribute: {}", err);
None
}
})
.collect();
Ok(ElementAttributes::new(new_attributes))
}
}
impl Parse for ElementAttributes {
fn parse(input: ParseStream) -> Result<Self> {
let mut attributes: HashSet<ElementAttribute> = HashSet::new();
while input.peek(syn::Ident::peek_any) {
let attribute = input.parse::<ElementAttribute>()?;
let ident = attribute.ident();
if attributes.contains(&attribute) {
emit_error!(
ident.span(),
"There is a previous definition of the {} attribute",
quote!(#ident)
);
}
attributes.insert(attribute);
}
Ok(ElementAttributes::new(attributes))
}
}
pub struct CustomElementAttributes<'a, 'c> {
attributes: &'a Attributes,
children: &'c Children,
}
impl<'a, 'c> ToTokens for CustomElementAttributes<'a, 'c> {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let mut attrs: Vec<_> = self
.attributes
.iter()
.map(|attribute| {
let ident = attribute.ident();
let value = attribute.value_tokens();
quote! {
#ident: #value
}
})
.collect();
if self.children.len() > 0 {
let children_tuple = self.children.as_option_of_tuples_tokens();
attrs.push(quote! {
children: #children_tuple
});
}
let quoted = if attrs.len() == 0 {
quote!()
} else {
quote!({ #(#attrs),* })
};
quoted.to_tokens(tokens);
}
}
pub struct SimpleElementAttributes<'a> {
attributes: &'a Attributes,
}
impl<'a> ToTokens for SimpleElementAttributes<'a> {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
if self.attributes.is_empty() {
quote!(None).to_tokens(tokens);
} else {
let attrs: Vec<_> = self
.attributes
.iter()
.map(|attribute| {
let mut iter = attribute.ident().iter();
let first_word = iter.next().unwrap().unraw();
let ident = iter.fold(first_word.to_string(), |acc, curr| {
format!("{}-{}", acc, curr.unraw())
});
let value = attribute.value_tokens();
quote! {
hm.insert(#ident, ::std::borrow::Cow::from(#value));
}
})
.collect();
let hashmap_declaration = quote! {{
let mut hm = std::collections::HashMap::<&str, ::std::borrow::Cow<'_, str>>::new();
#(#attrs)*
Some(hm)
}};
hashmap_declaration.to_tokens(tokens);
}
}
}