#![allow(bad_style)]
use proc_macro2::{Ident, Literal, Span, TokenStream};
use quote::{format_ident, quote, ToTokens};
use syn::{GenericParam, Generics, ItemStruct, Lifetime, LifetimeDef, TypeParamBound};
pub fn Leaf(structure: &ItemStruct) -> TokenStream {
impl_widget(structure, WidgetKind::Leaf)
}
pub fn SingleChild(structure: &ItemStruct) -> TokenStream {
impl_widget(structure, WidgetKind::SingleChild)
}
pub fn MultiChild(structure: &ItemStruct) -> TokenStream {
impl_widget(structure, WidgetKind::MultiChild)
}
pub fn View(structure: &ItemStruct) -> TokenStream {
impl_widget(structure, WidgetKind::View)
}
pub fn Inherited(structure: &ItemStruct) -> TokenStream {
impl_widget(structure, WidgetKind::Inherited)
}
fn impl_widget(input: &ItemStruct, kind: WidgetKind) -> TokenStream {
let name = &input.ident;
let (LT, Widget, WidgetKind, CheapEq, _) = imports();
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let WidgetKindVariant = kind.into_token();
let (cheap_to_cmp, eq_impl) = eq_impl(input.clone());
let WidgetDeriveImpl = widget_derive_impl(kind, input, name);
let (UniqueTypeId, _, _, _) = widget_derive_helpers(name, &input.generics);
quote! {
#[doc(hidden)]
pub enum #UniqueTypeId {}
impl #impl_generics #Widget for #name #ty_generics #where_clause {
fn unique_type(&self) -> ::std::any::TypeId {
::std::any::TypeId::of::<#UniqueTypeId>()
}
fn kind<#LT>(&#LT self) -> #WidgetKind {
#WidgetKind::#WidgetKindVariant(self)
}
}
#WidgetDeriveImpl
unsafe impl #impl_generics #CheapEq for #name #ty_generics #where_clause {
const CHEAP_TO_EQ: bool = #cheap_to_cmp;
fn cheap_eq(&self, other: &Self) -> bool {
#eq_impl
}
}
}
}
fn widget_derive_impl(kind: WidgetKind, input: &ItemStruct, name: &Ident) -> TokenStream {
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let (LT, Widget, _, _, WidgetDerive) = imports();
let (UniqueTypeId, WidgetAlias, AliasImplBounds, AliasTypeBounds) =
widget_derive_helpers(name, &input.generics);
let AliasTypeBounds = AliasTypeBounds.split_for_impl().1;
match kind {
WidgetKind::Leaf => quote! {
impl #impl_generics #WidgetDerive for #name #ty_generics #where_clause {
type Widget<#LT> = () where Self: #LT;
type UniqueTypeId = #UniqueTypeId;
}
},
_ => quote! {
#[doc(hidden)]
type #WidgetAlias #AliasImplBounds = impl #Widget + #LT;
impl #impl_generics #WidgetDerive for #name #ty_generics #where_clause {
type Widget<#LT> = #WidgetAlias #AliasTypeBounds where Self: #LT;
type UniqueTypeId = #UniqueTypeId;
}
},
}
}
fn widget_derive_helpers(name: &Ident, gparam: &Generics) -> (Ident, Ident, Generics, Generics) {
let UniqueTypeId = format_ident!("Asdavbmwqeryuiopvzxc{}{}", "UniqueTypeId", name);
let alias_ident = format_ident!("Asdavbmwqeryuiopvzxc{}{}", "WidgetAlias", name);
let mut impl_param = gparam.clone();
for param in impl_param.type_params_mut() {
param.bounds.push(TypeParamBound::Lifetime(Lifetime::new(
"'frui",
Span::call_site(),
)));
}
impl_param
.params
.push(GenericParam::Lifetime(LifetimeDef::new(Lifetime::new(
"'frui",
Span::call_site(),
))));
let mut type_param = gparam.clone();
type_param
.params
.push(GenericParam::Lifetime(LifetimeDef::new(Lifetime::new(
"'frui",
Span::call_site(),
))));
(UniqueTypeId, alias_ident, impl_param, type_param)
}
fn eq_impl(input: ItemStruct) -> (bool, TokenStream) {
let (_, _, _, CheapEq, _) = imports();
let fields_cheap_to_eq = input.fields.iter().map(|t| {
let ty = &t.ty;
quote!(<#ty as #CheapEq>::CHEAP_TO_EQ &&)
});
let fields_eq = input.fields.iter().enumerate().map(|(n, t)| {
let field_ident = t.ident.clone().map_or(
Literal::usize_unsuffixed(n).to_token_stream(),
|v| v.to_token_stream(),
);
quote!(#CheapEq::cheap_eq(&self.#field_ident, &other.#field_ident) &&)
});
let eq_impl = quote! (#(#fields_cheap_to_eq)* #(#fields_eq)* true);
let cheap_to_cmp = input.fields.len() == 0;
(cheap_to_cmp, eq_impl)
}
fn imports() -> (
TokenStream,
TokenStream,
TokenStream,
TokenStream,
TokenStream,
) {
let prelude = quote! { ::frui::prelude };
let Widget = quote! { #prelude::Widget };
let WidgetKind = quote! { #prelude::WidgetKind };
let LT = quote! { 'frui };
let macro_path = quote! { ::frui::macro_exports };
let CheapEq = quote! { #macro_path::CheapEq };
let MissingWidgetDerive = quote! { #macro_path::WidgetDerive };
(LT, Widget, WidgetKind, CheapEq, MissingWidgetDerive)
}
#[derive(Debug, Clone, Copy)]
enum WidgetKind {
Leaf,
SingleChild,
MultiChild,
View,
Inherited,
}
impl WidgetKind {
fn into_token(self) -> TokenStream {
match self {
WidgetKind::Leaf => quote! { Leaf },
WidgetKind::SingleChild => quote! { SingleChild },
WidgetKind::MultiChild => quote! { MultiChild },
WidgetKind::View => quote! { View },
WidgetKind::Inherited => quote! { Inherited },
}
}
}