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

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()
}
}