|
@@ -1,6 +1,7 @@
|
|
|
use proc_macro2::TokenStream;
|
|
|
use quote::{format_ident, quote, ToTokens, TokenStreamExt};
|
|
|
use syn::parse::{Parse, ParseStream};
|
|
|
+use syn::punctuated::Punctuated;
|
|
|
use syn::spanned::Spanned;
|
|
|
use syn::*;
|
|
|
|
|
@@ -55,11 +56,13 @@ impl ToTokens for ComponentBody {
|
|
|
// Props declared, so we generate a props struct and then also attach the doc attributes to it
|
|
|
false => {
|
|
|
let doc = format!("Properties for the [`{}`] component.", &comp_fn.sig.ident);
|
|
|
- let props_struct = self.props_struct();
|
|
|
+ let (props_struct, props_impls) = self.props_struct();
|
|
|
quote! {
|
|
|
#[doc = #doc]
|
|
|
#[allow(missing_docs)]
|
|
|
#props_struct
|
|
|
+
|
|
|
+ #(#props_impls)*
|
|
|
}
|
|
|
}
|
|
|
};
|
|
@@ -216,7 +219,7 @@ impl ComponentBody {
|
|
|
///
|
|
|
/// We try our best to transfer over any declared doc attributes from the original function signature onto the
|
|
|
/// props struct fields.
|
|
|
- fn props_struct(&self) -> ItemStruct {
|
|
|
+ fn props_struct(&self) -> (ItemStruct, Vec<ItemImpl>) {
|
|
|
let ItemFn { vis, sig, .. } = &self.item_fn;
|
|
|
let Signature {
|
|
|
inputs,
|
|
@@ -225,17 +228,56 @@ impl ComponentBody {
|
|
|
..
|
|
|
} = sig;
|
|
|
|
|
|
+ let generic_arguments = if !generics.params.is_empty() {
|
|
|
+ let generic_arguments = generics
|
|
|
+ .params
|
|
|
+ .iter()
|
|
|
+ .map(make_prop_struct_generics)
|
|
|
+ .collect::<Punctuated<_, Token![,]>>();
|
|
|
+ quote! { <#generic_arguments> }
|
|
|
+ } else {
|
|
|
+ quote! {}
|
|
|
+ };
|
|
|
let where_clause = &generics.where_clause;
|
|
|
let struct_fields = inputs.iter().map(move |f| make_prop_struct_field(f, vis));
|
|
|
+ let struct_field_idents = inputs
|
|
|
+ .iter()
|
|
|
+ .map(make_prop_struct_field_idents)
|
|
|
+ .collect::<Vec<_>>();
|
|
|
let struct_ident = Ident::new(&format!("{ident}Props"), ident.span());
|
|
|
|
|
|
- parse_quote! {
|
|
|
- #[derive(Props, Clone, PartialEq)]
|
|
|
+ let item_struct = parse_quote! {
|
|
|
+ #[derive(Props)]
|
|
|
#[allow(non_camel_case_types)]
|
|
|
#vis struct #struct_ident #generics #where_clause {
|
|
|
#(#struct_fields),*
|
|
|
}
|
|
|
- }
|
|
|
+ };
|
|
|
+
|
|
|
+ let item_impl_clone = parse_quote! {
|
|
|
+ impl #generics ::core::clone::Clone for #struct_ident #generic_arguments #where_clause {
|
|
|
+ #[inline]
|
|
|
+ fn clone(&self) -> Self {
|
|
|
+ Self {
|
|
|
+ #(#struct_field_idents: ::core::clone::Clone::clone(&self.#struct_field_idents)),*
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ let item_impl_partial_eq = parse_quote! {
|
|
|
+ impl #generics ::core::cmp::PartialEq for #struct_ident #generic_arguments #where_clause {
|
|
|
+ #[inline]
|
|
|
+ fn eq(&self, other: &Self) -> bool {
|
|
|
+ #(
|
|
|
+ self.#struct_field_idents == other.#struct_field_idents &&
|
|
|
+ )*
|
|
|
+ true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ (item_struct, vec![item_impl_clone, item_impl_partial_eq])
|
|
|
}
|
|
|
|
|
|
/// Convert a list of function arguments into a list of doc attributes for the props struct
|
|
@@ -494,6 +536,36 @@ fn make_prop_struct_field(f: &FnArg, vis: &Visibility) -> TokenStream {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/// Get ident from a function arg
|
|
|
+fn make_prop_struct_field_idents(f: &FnArg) -> &Ident {
|
|
|
+ // There's no receivers (&self) allowed in the component body
|
|
|
+ let FnArg::Typed(pt) = f else { unreachable!() };
|
|
|
+
|
|
|
+ match pt.pat.as_ref() {
|
|
|
+ // rip off mutability
|
|
|
+ // todo: we actually don't want any of the extra bits of the field pattern
|
|
|
+ Pat::Ident(f) => &f.ident,
|
|
|
+ _ => unreachable!(),
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+fn make_prop_struct_generics(generics: &GenericParam) -> TokenStream {
|
|
|
+ match generics {
|
|
|
+ GenericParam::Type(ty) => {
|
|
|
+ let ident = &ty.ident;
|
|
|
+ quote! { #ident }
|
|
|
+ }
|
|
|
+ GenericParam::Lifetime(lifetime) => {
|
|
|
+ let lifetime = &lifetime.lifetime;
|
|
|
+ quote! { #lifetime }
|
|
|
+ }
|
|
|
+ GenericParam::Const(c) => {
|
|
|
+ let ident = &c.ident;
|
|
|
+ quote! { #ident }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
fn rebind_mutability(f: &FnArg) -> TokenStream {
|
|
|
// There's no receivers (&self) allowed in the component body
|
|
|
let FnArg::Typed(pt) = f else { unreachable!() };
|