use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Field, Fields, ItemStruct, LitStr};
use titan_utils::validatecss::{validate_css, CSSValidationError};
#[proc_macro]
pub fn css(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as LitStr);
let result = input.value();
if let Err(err) = validate_css(&result) {
match err {
CSSValidationError::FieldError(field) => {
let span = input.span();
let error_msg = format!("Invalid css property name: {}", field);
let err = syn::Error::new(span, error_msg);
return err.to_compile_error().into(); }
CSSValidationError::EntireFile(location) => {
let span = input.span();
let error_msg = format!(
"Error parsing css at line = {}, col = {}",
location.line, location.column
);
let err = syn::Error::new(span, error_msg);
return err.to_compile_error().into(); }
}
};
let expanded = quote! {
#result
};
TokenStream::from(expanded)
}
#[proc_macro]
pub fn html_tag(item: TokenStream) -> TokenStream {
let mut item_struct = syn::parse::<ItemStruct>(item).unwrap();
let struct_name = &item_struct.ident;
let new_field: Vec<Field> = Vec::from_iter([
syn::parse_quote! { pub classes: std::collections::HashSet<crate::tags::TagClass> },
syn::parse_quote! { pub ids: Vec<String> },
syn::parse_quote! { pub attributes: HashMap<String, String> },
]);
let Fields::Named(fields_named) = &mut item_struct.fields else {
return syn::Error::new_spanned(
item_struct,
"Only named fields are supported",
)
.to_compile_error()
.into();
};
for field in new_field {
fields_named.named.push(field);
}
let expanded = quote! {
#[derive(Clone)]
#item_struct
impl #struct_name {
pub fn add_class(mut self, class: crate::tags::TagClass) -> Self {
self.classes.insert(class);
self
}
pub fn add_id(mut self, id: impl Into<String>) -> Self {
self.ids.push(id.into());
self
}
pub fn add_attribute(mut self, key: String, value: String) -> Self {
self.attributes.insert(key, value);
self
}
pub fn styles(mut self, str_styles: &str) -> Self {
if let Err(err) = crate::prelude::__private_validatecss::validate_css(&str_styles) {
match err {
crate::prelude::__private_validatecss::CSSValidationError::FieldError(field) => {
panic!("Invalid css property name: {}", field);
}
crate::prelude::__private_validatecss::CSSValidationError::EntireFile(location) => {
panic!(
"Error parsing css at line = {}, col = {}",
location.line, location.column
);
}
}
};
self.add_class(crate::tags::TagClass::Style(str_styles.to_string()))
}
}
};
TokenStream::from(expanded)
}