Răsfoiți Sursa

Merge pull request #324 from overlisted/inlineprops-generics

`#[inline_props]` generics
Jon Kelley 3 ani în urmă
părinte
comite
4edaeb0aae
3 a modificat fișierele cu 90 adăugiri și 17 ștergeri
  1. 9 8
      examples/README.md
  2. 41 0
      examples/inlineprops.rs
  3. 40 9
      packages/core-macro/src/inlineprops.rs

+ 9 - 8
examples/README.md

@@ -41,6 +41,7 @@ These examples are not necessarily meant to be run, but rather serve as a refere
 | [Anti-patterns](./antipatterns.rs)                  | A collection of discouraged patterns            | ✅      |
 | [Complete rsx reference](./rsx_usage.rs)            | A complete reference for all rsx! usage         | ✅      |
 | [Event Listeners](./listener.rs)                    | Attach closures to events on elements           | ✅      |
+| [Inline Props](./inlineprops.rs)                    | Using the `#[inline_props]` macro               | ✅      |
 
 
 ## Show me some examples!
@@ -51,7 +52,7 @@ In our collection of examples, guides, and tutorials, we have:
 - The reference (a collection of examples with heavy documentation)
 - The general examples
 - The platform-specific examples (web, ssr, desktop, mobile, server)
-  
+
 Here's what a few common tasks look like in Dioxus:
 
 Nested components with children and internal state:
@@ -80,7 +81,7 @@ Controlled inputs:
 ```rust
 fn App(cx: Scope) -> Element {
   let value = use_state(&cx, String::new);
-  cx.render(rsx!( 
+  cx.render(rsx!(
     input {
       "type": "text",
       value: "{value}",
@@ -96,16 +97,16 @@ fn App(cx: Scope) -> Element {
   let list = (0..10).map(|i| {
     rsx!(li { key: "{i}", "Value: {i}" })
   });
-  
+
   let title = match list.len() {
     0 => rsx!("Not enough"),
     _ => rsx!("Plenty!"),
   };
 
   if should_show {
-    cx.render(rsx!( 
+    cx.render(rsx!(
       title,
-      ul { list } 
+      ul { list }
     ))
   } else {
     None
@@ -165,18 +166,18 @@ fn App(cx: Scope) -> Element {
       Route::Home => rsx!( Home {} ),
       Route::Post(id) => rsx!( Post { id: id })
     }
-  }))  
+  }))
 }
 ```
 
-Suspense 
+Suspense
 ```rust
 fn App(cx: Scope) -> Element {
   let doggo = use_suspense(cx,
     || async { reqwest::get("https://dog.ceo/api/breeds/image/random").await.unwrap().json::<Response>().await.unwrap() },
     |response| cx.render(rsx!( img { src: "{response.message}" }))
   );
-  
+
   cx.render(rsx!{
     div {
       "One doggo coming right up:",

+ 41 - 0
examples/inlineprops.rs

@@ -0,0 +1,41 @@
+//! Run with `cargo-expand` to see what each one expands to
+#![allow(non_snake_case)]
+
+use dioxus::prelude::*;
+
+#[inline_props]
+fn Thing1<T>(cx: Scope, _a: T) -> Element {
+    cx.render(rsx! { "" })
+}
+
+#[inline_props]
+fn Thing2(cx: Scope, _a: u32) -> Element<'a> {
+    cx.render(rsx! { "" })
+}
+
+#[inline_props]
+fn Thing3<'a, T>(cx: Scope<'a>, _a: &'a T) -> Element<'a> {
+    cx.render(rsx! { "" })
+}
+
+#[inline_props]
+fn Thing4<'a>(cx: Scope<'a>, _a: &'a u32) -> Element<'a> {
+    cx.render(rsx! { "" })
+}
+
+fn main() {
+    dioxus::desktop::launch(app);
+}
+
+fn app(cx: Scope) -> Element {
+    let state = use_state(&cx, || 1);
+
+    cx.render(rsx! {
+        div {
+            Thing1 { _a: 1 },
+            Thing2 { _a: 1 },
+            Thing3 { _a: state },
+            Thing4 { _a: state },
+        }
+    })
+}

+ 40 - 9
packages/core-macro/src/inlineprops.rs

@@ -3,7 +3,7 @@ use quote::{quote, ToTokens, TokenStreamExt};
 use syn::{
     parse::{Parse, ParseStream},
     punctuated::Punctuated,
-    token, Block, FnArg, Generics, Ident, Pat, Result, ReturnType, Token, Visibility,
+    *,
 };
 
 pub struct InlinePropsBody {
@@ -34,7 +34,7 @@ impl Parse for InlinePropsBody {
         let first_arg: FnArg = content.parse()?;
         let cx_token = {
             match first_arg {
-                FnArg::Receiver(_) => panic!("first argument must not be  a reciver argument"),
+                FnArg::Receiver(_) => panic!("first argument must not be a receiver argument"),
                 FnArg::Typed(f) => f.pat,
             }
         };
@@ -86,26 +86,57 @@ impl ToTokens for InlinePropsBody {
             FnArg::Typed(t) => Some(&t.pat),
         });
 
-        let modifiers = if generics.params.is_empty() {
-            quote! { #[derive(Props, PartialEq)] }
+        let first_lifetime = if let Some(GenericParam::Lifetime(lt)) = generics.params.first() {
+            Some(lt)
         } else {
+            None
+        };
+
+        let modifiers = if first_lifetime.is_some() {
             quote! { #[derive(Props)] }
+        } else {
+            quote! { #[derive(Props, PartialEq)] }
         };
 
-        let lifetime = if generics.params.is_empty() {
-            quote! {}
+        let (scope_lifetime, fn_generics, struct_generics) = if let Some(lt) = first_lifetime {
+            let struct_generics: Punctuated<_, token::Comma> = generics
+                .params
+                .iter()
+                .map(|it| match it {
+                    GenericParam::Type(tp) => {
+                        let mut tp = tp.clone();
+                        tp.bounds.push(parse_quote!( 'a ));
+
+                        GenericParam::Type(tp)
+                    }
+                    _ => it.clone(),
+                })
+                .collect();
+
+            (
+                quote! { #lt, },
+                generics.clone(),
+                quote! { <#struct_generics> },
+            )
         } else {
-            quote! { 'a, }
+            let lifetime: LifetimeDef = parse_quote! { 'a };
+
+            let mut fn_generics = generics.clone();
+            fn_generics
+                .params
+                .insert(0, GenericParam::Lifetime(lifetime.clone()));
+
+            (quote! { #lifetime, }, fn_generics, quote! { #generics })
         };
 
         out_tokens.append_all(quote! {
             #modifiers
             #[allow(non_camel_case_types)]
-            #vis struct #struct_name #generics {
+            #vis struct #struct_name #struct_generics {
                 #(#fields),*
             }
 
-            #vis fn #ident #generics (#cx_token: Scope<#lifetime #struct_name #generics>) #output {
+            #vis fn #ident #fn_generics (#cx_token: Scope<#scope_lifetime #struct_name #generics>) #output {
                 let #struct_name { #(#field_names),* } = &cx.props;
                 #block
             }