mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
parent
aa49ad760b
commit
66d7cb5d12
5 changed files with 179 additions and 0 deletions
|
@ -19,6 +19,7 @@ mod params;
|
||||||
mod view;
|
mod view;
|
||||||
use crate::component::unmodified_fn_name_from_fn_name;
|
use crate::component::unmodified_fn_name_from_fn_name;
|
||||||
mod component;
|
mod component;
|
||||||
|
mod memo;
|
||||||
mod slice;
|
mod slice;
|
||||||
mod slot;
|
mod slot;
|
||||||
|
|
||||||
|
@ -875,3 +876,35 @@ pub fn params_derive(
|
||||||
pub fn slice(input: TokenStream) -> TokenStream {
|
pub fn slice(input: TokenStream) -> TokenStream {
|
||||||
slice::slice_impl(input)
|
slice::slice_impl(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates a `memo` into a struct with a default getter.
|
||||||
|
///
|
||||||
|
/// Can be used to access deeply nested fields within a global state object.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use leptos::prelude::*;
|
||||||
|
/// # use leptos_macro::memo;
|
||||||
|
///
|
||||||
|
/// #[derive(Default)]
|
||||||
|
/// pub struct Outer {
|
||||||
|
/// count: i32,
|
||||||
|
/// inner: Inner,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// #[derive(Default)]
|
||||||
|
/// pub struct Inner {
|
||||||
|
/// inner_count: i32,
|
||||||
|
/// inner_name: String,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let outer_signal = RwSignal::new(Outer::default());
|
||||||
|
///
|
||||||
|
/// let count = memo!(outer_signal.count);
|
||||||
|
///
|
||||||
|
/// let inner_count = memo!(outer_signal.inner.inner_count);
|
||||||
|
/// let inner_name = memo!(outer_signal.inner.inner_name);
|
||||||
|
/// ```
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn memo(input: TokenStream) -> TokenStream {
|
||||||
|
memo::memo_impl(input)
|
||||||
|
}
|
||||||
|
|
56
leptos_macro/src/memo.rs
Normal file
56
leptos_macro/src/memo.rs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
extern crate proc_macro;
|
||||||
|
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
use quote::{quote, ToTokens};
|
||||||
|
use syn::{
|
||||||
|
parse::{Parse, ParseStream},
|
||||||
|
parse_macro_input,
|
||||||
|
punctuated::Punctuated,
|
||||||
|
Token,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MemoMacroInput {
|
||||||
|
root: syn::Ident,
|
||||||
|
path: Punctuated<syn::Member, Token![.]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for MemoMacroInput {
|
||||||
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
|
let root: syn::Ident = input.parse()?;
|
||||||
|
input.parse::<Token![.]>()?;
|
||||||
|
// do not accept trailing punctuation
|
||||||
|
let path: Punctuated<syn::Member, Token![.]> =
|
||||||
|
Punctuated::parse_separated_nonempty(input)?;
|
||||||
|
|
||||||
|
if path.is_empty() {
|
||||||
|
return Err(input.error("expected identifier"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !input.is_empty() {
|
||||||
|
return Err(input.error("unexpected token"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self { root, path })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for MemoMacroInput {
|
||||||
|
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||||
|
let root = &self.root;
|
||||||
|
let path = &self.path;
|
||||||
|
|
||||||
|
tokens.extend(quote! {
|
||||||
|
::leptos::reactive_graph::computed::Memo::new(
|
||||||
|
move |_| {
|
||||||
|
use ::leptos::reactive_graph::traits::With;
|
||||||
|
#root.with(|st: _| st.#path.clone())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn memo_impl(tokens: TokenStream) -> TokenStream {
|
||||||
|
let input = parse_macro_input!(tokens as MemoMacroInput);
|
||||||
|
input.into_token_stream().into()
|
||||||
|
}
|
33
leptos_macro/tests/memo.rs
Normal file
33
leptos_macro/tests/memo.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
use leptos::prelude::RwSignal;
|
||||||
|
use leptos_macro::memo;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct OuterState {
|
||||||
|
count: i32,
|
||||||
|
inner: InnerState,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Default)]
|
||||||
|
pub struct InnerState {
|
||||||
|
inner_count: i32,
|
||||||
|
inner_tuple: InnerTuple,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Default)]
|
||||||
|
pub struct InnerTuple(String);
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn green() {
|
||||||
|
let outer_signal = RwSignal::new(OuterState::default());
|
||||||
|
|
||||||
|
let _ = memo!(outer_signal.count);
|
||||||
|
|
||||||
|
let _ = memo!(outer_signal.inner.inner_count);
|
||||||
|
let _ = memo!(outer_signal.inner.inner_tuple.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn red() {
|
||||||
|
let t = trybuild::TestCases::new();
|
||||||
|
t.compile_fail("tests/memo/red.rs")
|
||||||
|
}
|
26
leptos_macro/tests/memo/red.rs
Normal file
26
leptos_macro/tests/memo/red.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
use leptos::prelude::RwSignal;
|
||||||
|
use leptos_macro::memo;
|
||||||
|
|
||||||
|
#[derive(Default, PartialEq)]
|
||||||
|
pub struct OuterState {
|
||||||
|
count: i32,
|
||||||
|
inner: InnerState,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Default)]
|
||||||
|
pub struct InnerState {
|
||||||
|
inner_count: i32,
|
||||||
|
inner_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let outer_signal = RwSignal::new(OuterState::default());
|
||||||
|
|
||||||
|
let _ = memo!();
|
||||||
|
|
||||||
|
let _ = memo!(outer_signal);
|
||||||
|
|
||||||
|
let _ = memo!(outer_signal.);
|
||||||
|
|
||||||
|
let _ = memo!(outer_signal.inner.);
|
||||||
|
}
|
31
leptos_macro/tests/memo/red.stderr
Normal file
31
leptos_macro/tests/memo/red.stderr
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
error: unexpected end of input, expected identifier
|
||||||
|
--> tests/memo/red.rs:19:13
|
||||||
|
|
|
||||||
|
19 | let _ = memo!();
|
||||||
|
| ^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `memo` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: expected `.`
|
||||||
|
--> tests/memo/red.rs:21:13
|
||||||
|
|
|
||||||
|
21 | let _ = memo!(outer_signal);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `memo` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: unexpected end of input, expected identifier or integer
|
||||||
|
--> tests/memo/red.rs:23:13
|
||||||
|
|
|
||||||
|
23 | let _ = memo!(outer_signal.);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `memo` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: unexpected end of input, expected identifier or integer
|
||||||
|
--> tests/memo/red.rs:25:13
|
||||||
|
|
|
||||||
|
25 | let _ = memo!(outer_signal.inner.);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `memo` (in Nightly builds, run with -Z macro-backtrace for more info)
|
Loading…
Reference in a new issue