Skip to content

Commit

Permalink
feat: #[component] now handles impl Trait by converting to generi…
Browse files Browse the repository at this point in the history
  • Loading branch information
MingweiSamuel committed May 21, 2024
1 parent a2c7e23 commit cbc19b5
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 6 deletions.
64 changes: 60 additions & 4 deletions leptos_macro/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ use leptos_hot_reload::parsing::value_to_string;
use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote, quote_spanned, ToTokens, TokenStreamExt};
use syn::{
parse::Parse, parse_quote, spanned::Spanned,
AngleBracketedGenericArguments, Attribute, FnArg, GenericArgument, Item,
ItemFn, LitStr, Meta, Pat, PatIdent, Path, PathArguments, ReturnType,
Signature, Stmt, Type, TypePath, Visibility,
parse::Parse, parse_quote, spanned::Spanned, token::Colon,
visit_mut::VisitMut, AngleBracketedGenericArguments, Attribute, FnArg,
GenericArgument, GenericParam, Item, ItemFn, LitStr, Meta, Pat, PatIdent,
Path, PathArguments, ReturnType, Signature, Stmt, Type, TypeImplTrait,
TypeParam, TypePath, Visibility,
};

pub struct Model {
Expand All @@ -28,6 +29,7 @@ pub struct Model {
impl Parse for Model {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut item = ItemFn::parse(input)?;
convert_impl_trait_to_generic(&mut item.sig);

let docs = Docs::new(&item.attrs);

Expand Down Expand Up @@ -1229,3 +1231,57 @@ fn is_valid_into_view_return_type(ty: &ReturnType) -> bool {
pub fn unmodified_fn_name_from_fn_name(ident: &Ident) -> Ident {
Ident::new(&format!("__{ident}"), ident.span())
}

/// Converts all `impl Trait`s in a function signature to use generic params instead.
pub fn convert_impl_trait_to_generic(sig: &mut Signature) {
fn new_generic_ident(i: usize, span: Span) -> Ident {
Ident::new(&format!("__ImplTrait{}", i), span)
}

// First: visit all `impl Trait`s and replace them with new generic params.
#[derive(Default)]
struct RemoveImplTrait(Vec<TypeImplTrait>);
impl VisitMut for RemoveImplTrait {
fn visit_type_mut(&mut self, ty: &mut Type) {
syn::visit_mut::visit_type_mut(self, ty);
if matches!(ty, Type::ImplTrait(_)) {
let ident = new_generic_ident(self.0.len(), ty.span());
let generic_type = Type::Path(TypePath {
qself: None,
path: Path::from(ident),
});
let Type::ImplTrait(impl_trait) =
std::mem::replace(ty, generic_type)
else {
unreachable!();
};
self.0.push(impl_trait);
}
}

// Early exits.
fn visit_attribute_mut(&mut self, _: &mut Attribute) {}
fn visit_pat_mut(&mut self, _: &mut Pat) {}
}
let mut visitor = RemoveImplTrait::default();
for fn_arg in sig.inputs.iter_mut() {
visitor.visit_fn_arg_mut(fn_arg);
}
let RemoveImplTrait(impl_traits) = visitor;

// Second: Add the new generic params into the signature.
for (i, impl_trait) in impl_traits.into_iter().enumerate() {
let span = impl_trait.span();
let ident = new_generic_ident(i, span);
// We can simply append to the end (only lifetime params must be first).
// Note currently default generics are not allowed in `fn`, so not a concern.
sig.generics.params.push(GenericParam::Type(TypeParam {
attrs: vec![],
ident,
colon_token: Some(Colon { spans: [span] }),
bounds: impl_trait.bounds,
eq_token: None,
default: None,
}));
}
}
6 changes: 5 additions & 1 deletion leptos_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ impl Default for Mode {

mod params;
mod view;
use crate::component::unmodified_fn_name_from_fn_name;
use component::{
convert_impl_trait_to_generic, unmodified_fn_name_from_fn_name,
};
use view::{client_template::render_template, render_view};
mod component;
mod slice;
Expand Down Expand Up @@ -608,6 +610,8 @@ pub fn component(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
let Ok(mut dummy) = syn::parse::<DummyModel>(s.clone()) else {
return s;
};
convert_impl_trait_to_generic(&mut dummy.sig);

let parse_result = syn::parse::<component::Model>(s);

if let (ref mut unexpanded, Ok(model)) = (&mut dummy, parse_result) {
Expand Down
9 changes: 8 additions & 1 deletion leptos_macro/tests/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,27 @@ fn Component(
#[prop(strip_option)] strip_option: Option<u8>,
#[prop(default = NonZeroUsize::new(10).unwrap())] default: NonZeroUsize,
#[prop(into)] into: String,
impl_trait: impl Fn() -> i32 + 'static,
) -> impl IntoView {
_ = optional;
_ = optional_no_strip;
_ = strip_option;
_ = default;
_ = into;
_ = impl_trait;
}

#[test]
fn component() {
let cp = ComponentProps::builder().into("").strip_option(9).build();
let cp = ComponentProps::builder()
.into("")
.strip_option(9)
.impl_trait(|| 42)
.build();
assert!(!cp.optional);
assert_eq!(cp.optional_no_strip, None);
assert_eq!(cp.strip_option, Some(9));
assert_eq!(cp.default, NonZeroUsize::new(10).unwrap());
assert_eq!(cp.into, "");
assert_eq!((cp.impl_trait)(), 42);
}

0 comments on commit cbc19b5

Please sign in to comment.