use crate::util::{define_ident, use_absolute}; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::{parse_macro_input, DeriveInput, GenericParam}; pub fn custom(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = ComponentDef::new(parse_macro_input!(input as DeriveInput)); let name = input.name_with_generics(); let generics = input.generic_predicates(); let where_clause_predicates = input.where_clause_predicates(); use_absolute! { uwui::backend::Backend as Backend, uwui::view::GetTemplate as GetTemplate, uwui::view::__internal::CustomComponent as CustomComponent, uwui::view::__internal::IntoNode as IntoNode, uwui::view::__internal::CustomComponentWrapper as CustomComponentWrapper, } define_ident! { B } let expanded = quote! { #[automatically_derived] impl<#B: #Backend, #generics> #CustomComponent<#B> for #name where #name : #GetTemplate<#B>, #where_clause_predicates {} #[automatically_derived] impl<#B: #Backend, #generics> #IntoNode<#B> for #name where #name : #GetTemplate<#B>, #where_clause_predicates { type Node = #CustomComponentWrapper<#B, Self>; fn into_node(self) -> Self::Node { #CustomComponentWrapper::new(self) } } }; expanded.into() } pub fn native(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = ComponentDef::new(parse_macro_input!(input as DeriveInput)); let name = input.name_with_generics(); let generics = input.generic_predicates(); let where_clause_predicates = input.where_clause_predicates(); use_absolute! { uwui::backend::Backend as Backend, uwui::backend::Render as Render, uwui::view::__internal::Node as Node, uwui::view::__internal::IntoNode as IntoNode, } define_ident! { B } let expanded = quote! { #[automatically_derived] impl<#B: #Backend, #generics> #Node<#B> for #name where #name : #Render<#B>, #where_clause_predicates {} #[automatically_derived] impl<#B: #Backend, #generics> #IntoNode<#B> for #name where #name : #Render<#B>, #where_clause_predicates { type Node = Self; fn into_node(self) -> Self::Node { self } } }; expanded.into() } struct ComponentDef { derive_input: DeriveInput, } impl ComponentDef { pub fn new(derive_input: DeriveInput) -> Self { Self { derive_input } } /// `Name` pub fn name_with_generics(&self) -> TokenStream { let name = &self.derive_input.ident; let generics = self.generic_names(); if generics.is_empty() { name.to_token_stream() } else { quote! { #name < #generics > } } } /// `T1, T2, T3` pub fn generic_names(&self) -> TokenStream { let mut out = Vec::new(); let generics = &self.derive_input.generics; for param in generics.params.iter() { match param { GenericParam::Lifetime(lft) => { out.push(lft.lifetime.to_token_stream()); } GenericParam::Type(typ) => { out.push(typ.ident.to_token_stream()); } GenericParam::Const(cnst) => { out.push(cnst.ident.to_token_stream()); } } } quote! { #(#out),* } } /// `T1: Constraint1, T2: Constraint2 + Constraint1, T3` pub fn generic_predicates(&self) -> TokenStream { let mut out = TokenStream::new(); let generics = &self.derive_input.generics; for param in generics.params.iter() { match param { GenericParam::Lifetime(lft) => { lft.lifetime.to_tokens(&mut out); if let Some(colon) = lft.colon_token { let bounds = &lft.bounds; out.extend(quote! { #colon #bounds }); } out.extend(quote! { , }); } GenericParam::Type(typ) => { typ.ident.to_tokens(&mut out); if let Some(colon) = typ.colon_token { let bounds = &typ.bounds; out.extend(quote! { #colon #bounds }); } out.extend(quote! { , }); } GenericParam::Const(cnst) => { let kw = &cnst.const_token; let ident = &cnst.ident; let colon = &cnst.colon_token; let ty = &cnst.ty; out.extend(quote! { #kw #ident #colon #ty , }); } } } out } pub fn where_clause_predicates(&self) -> TokenStream { self.derive_input .generics .where_clause .as_ref() .map(|clause| &clause.predicates) .to_token_stream() } }