Skip to content

Commit

Permalink
feat: add a slice!() macro (leptos-rs#1867)
Browse files Browse the repository at this point in the history
  • Loading branch information
SadraMoh authored Oct 24, 2023
1 parent a7330d6 commit 30370a5
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 2 deletions.
36 changes: 35 additions & 1 deletion leptos_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ mod view;
use view::{client_template::render_template, render_view};
mod component;
mod server;
mod slice;
mod slot;

/// The `view` macro uses RSX (like JSX, but Rust!) It follows most of the
Expand Down Expand Up @@ -958,7 +959,7 @@ pub fn params_derive(
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
match syn::parse(input) {
Ok(ast) => params::impl_params(&ast),
Ok(ast) => params::params_impl(&ast),
Err(err) => err.to_compile_error().into(),
}
}
Expand All @@ -969,3 +970,36 @@ pub(crate) fn attribute_value(attr: &KeyedAttribute) -> &syn::Expr {
None => abort!(attr.key, "attribute should have value"),
}
}

/// Generates a `lens` into struct with a default getter and setter
///
/// Can be used to access deeply nested fields within a global state object
///
/// ```rust
/// # use leptos::{create_runtime, create_rw_signal};
/// # use leptos_macro::slice;
/// # let runtime = create_runtime();
///
/// #[derive(Default)]
/// pub struct Outer {
/// count: i32,
/// inner: Inner,
/// }
///
/// #[derive(Default)]
/// pub struct Inner {
/// inner_count: i32,
/// inner_name: String,
/// }
///
/// let outer_signal = create_rw_signal(Outer::default());
///
/// let (count, set_count) = slice!(outer_signal.count);
///
/// let (inner_count, set_inner_count) = slice!(outer_signal.inner.inner_count);
/// let (inner_name, set_inner_name) = slice!(outer_signal.inner.inner_name);
/// ```
#[proc_macro]
pub fn slice(input: TokenStream) -> TokenStream {
slice::slice_impl(input)
}
2 changes: 1 addition & 1 deletion leptos_macro/src/params.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use quote::{quote, quote_spanned};
use syn::spanned::Spanned;

pub fn impl_params(ast: &syn::DeriveInput) -> proc_macro::TokenStream {
pub fn params_impl(ast: &syn::DeriveInput) -> proc_macro::TokenStream {
let name = &ast.ident;

let fields = if let syn::Data::Struct(syn::DataStruct {
Expand Down
59 changes: 59 additions & 0 deletions leptos_macro/src/slice.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
extern crate proc_macro;

use proc_macro::TokenStream;
use proc_macro2::Ident;
use quote::quote;
use syn::{
parse::{Parse, ParseStream},
parse_macro_input,
punctuated::Punctuated,
token::Dot,
Token, Type,
};

struct SliceMacroInput {
pub root: Ident,
pub path: Punctuated<Type, Dot>,
}

impl Parse for SliceMacroInput {
fn parse(input: ParseStream) -> Result<Self, syn::Error> {
let root = syn::Ident::parse(input)?;
let _dot = <Token![.]>::parse(input)?;
let path = input.parse_terminated(Type::parse, Token![.])?;

if path.is_empty() {
return Err(syn::Error::new(input.span(), "Expected identifier"));
}

if path.trailing_punct() {
return Err(syn::Error::new(
input.span(),
"Unexpected trailing `.`",
));
}

Ok(SliceMacroInput { root, path })
}
}

impl From<SliceMacroInput> for TokenStream {
fn from(val: SliceMacroInput) -> Self {
let root = val.root;
let path = val.path;

quote! {
::leptos::create_slice(
#root,
|st: &_| st.#path.clone(),
|st: &mut _, n| st.#path = n
)
}
.into()
}
}

pub fn slice_impl(tokens: TokenStream) -> TokenStream {
let input = parse_macro_input!(tokens as SliceMacroInput);
input.into()
}
32 changes: 32 additions & 0 deletions leptos_macro/tests/slice.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use leptos::{create_runtime, create_rw_signal};
use leptos_macro::slice;

#[derive(Default)]
pub struct OuterState {
count: i32,
inner: InnerState,
}

#[derive(Clone, PartialEq, Default)]
pub struct InnerState {
inner_count: i32,
inner_name: String,
}

#[test]
fn green() {
let _ = create_runtime();

let outer_signal = create_rw_signal(OuterState::default());

let (_, _) = slice!(outer_signal.count);

let (_, _) = slice!(outer_signal.inner.inner_count);
let (_, _) = slice!(outer_signal.inner.inner_name);
}

#[test]
fn red() {
let t = trybuild::TestCases::new();
t.compile_fail("tests/slice/red.rs")
}
28 changes: 28 additions & 0 deletions leptos_macro/tests/slice/red.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use leptos::{create_runtime, create_rw_signal};
use leptos_macro::slice;

#[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 _ = create_runtime();

let outer_signal = create_rw_signal(OuterState::default());

let (_, _) = slice!();

let (_, _) = slice!(outer_signal);

let (_, _) = slice!(outer_signal.);

let (_, _) = slice!(outer_signal.inner.);
}
31 changes: 31 additions & 0 deletions leptos_macro/tests/slice/red.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
error: unexpected end of input, expected identifier
--> tests/slice/red.rs:21:18
|
21 | let (_, _) = slice!();
| ^^^^^^^^
|
= note: this error originates in the macro `slice` (in Nightly builds, run with -Z macro-backtrace for more info)

error: expected `.`
--> tests/slice/red.rs:23:18
|
23 | let (_, _) = slice!(outer_signal);
| ^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `slice` (in Nightly builds, run with -Z macro-backtrace for more info)

error: Expected identifier
--> tests/slice/red.rs:25:18
|
25 | let (_, _) = slice!(outer_signal.);
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `slice` (in Nightly builds, run with -Z macro-backtrace for more info)

error: Unexpected trailing `.`
--> tests/slice/red.rs:27:18
|
27 | let (_, _) = slice!(outer_signal.inner.);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `slice` (in Nightly builds, run with -Z macro-backtrace for more info)

0 comments on commit 30370a5

Please sign in to comment.