You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
180 lines
5.3 KiB
Rust
180 lines
5.3 KiB
Rust
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<T1, T2, T3>`
|
|
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<T1> + 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()
|
|
}
|
|
}
|