diff --git a/Cargo.toml b/Cargo.toml index 3c76749..f5a4235 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "osui-element" -version = "0.1.19" +version = "0.1.20" edition = "2021" description = "The element attribute for defining elements in OSUI" license = "Apache-2.0" diff --git a/lib.rs b/lib.rs index 6a93e58..0aa400f 100644 --- a/lib.rs +++ b/lib.rs @@ -1,7 +1,94 @@ use proc_macro::TokenStream; use proc_macro2::Span; use quote::quote; -use syn::{parse_macro_input, FnArg, ItemFn, Lifetime, PatIdent, PatType, Type, TypeReference}; +use syn::{ + parse::Parser, parse_macro_input, DeriveInput, FnArg, ItemFn, Lifetime, LifetimeParam, + PatIdent, PatType, Type, TypeReference, +}; + +#[proc_macro_attribute] +pub fn element(_args: TokenStream, input: TokenStream) -> TokenStream { + let mut ast = parse_macro_input!(input as DeriveInput); + let struct_name = &ast.ident; + + // Check if the struct has generics + let has_generics = !ast.generics.params.is_empty(); + + // If there are no generics, add a lifetime (if necessary) + if ast.generics.lifetimes().count() == 0 && !has_generics { + ast.generics + .params + .push(syn::GenericParam::Lifetime(LifetimeParam::new( + Lifetime::new("'a", proc_macro2::Span::call_site()), + ))); + } + + // Modify the struct fields to include additional fields + match &mut ast.data { + syn::Data::Struct(ref mut struct_data) => { + if let syn::Fields::Named(fields) = &mut struct_data.fields { + fields.named.push( + syn::Field::parse_named + .parse2(quote! { pub children: Children }) + .unwrap(), + ); + fields.named.push( + syn::Field::parse_named + .parse2(quote! { pub style: Style }) + .unwrap(), + ); + fields.named.push( + syn::Field::parse_named + .parse2(quote! { pub id: &'a str }) + .unwrap(), + ); + fields.named.push( + syn::Field::parse_named + .parse2(quote! { pub class: &'a str }) + .unwrap(), + ); + } + } + _ => panic!("`element` can only be used with structs"), + } + + // Generate the impl block with generics if needed + let impl_block = quote! { + impl<'a> ElementCore for #struct_name<'a> { + fn get_element_by_id(&mut self, id: &str) -> Option<&mut Element> { + if let Children::Children(children, _) = &mut self.children { + for elem in children { + if elem.get_id() == id { + return Some(elem); + } else if let Some(e) = elem.get_element_by_id(id) { + return Some(e); + } + } + } + None + } + + fn get_id(&self) -> String { + self.id.to_string() + } + + fn get_class(&self) -> String { + self.class.to_string() + } + + fn get_style(&self) -> &Style { + &self.style + } + } + }; + + let expanded = quote! { + #ast + #impl_block + }; + + expanded.into() +} #[proc_macro_attribute] pub fn component(_args: TokenStream, input: TokenStream) -> TokenStream {