From f2117b1186ac2db7bbb15d4319019994d1229f7e Mon Sep 17 00:00:00 2001 From: Greg Johnston Date: Wed, 20 Sep 2023 19:37:39 -0400 Subject: [PATCH 1/2] fix: restore missing `run_as_child` --- leptos_reactive/src/lib.rs | 4 ++-- leptos_reactive/src/runtime.rs | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/leptos_reactive/src/lib.rs b/leptos_reactive/src/lib.rs index 7367ab737c..a7dc6bbf3b 100644 --- a/leptos_reactive/src/lib.rs +++ b/leptos_reactive/src/lib.rs @@ -114,8 +114,8 @@ pub use resource::*; use runtime::*; pub use runtime::{ as_child_of_current_owner, batch, create_runtime, current_runtime, - on_cleanup, set_current_runtime, untrack, untrack_with_diagnostics, - with_current_owner, with_owner, Owner, RuntimeId, + on_cleanup, run_as_child, set_current_runtime, untrack, + untrack_with_diagnostics, with_current_owner, with_owner, Owner, RuntimeId, }; pub use selector::*; pub use serialization::*; diff --git a/leptos_reactive/src/runtime.rs b/leptos_reactive/src/runtime.rs index 94a825bee8..486ceb4d74 100644 --- a/leptos_reactive/src/runtime.rs +++ b/leptos_reactive/src/runtime.rs @@ -838,6 +838,40 @@ where .expect("runtime should be alive when with_owner runs") } +/// Runs the given function as a child of the current Owner, once. +pub fn run_as_child(f: impl FnOnce() -> T + 'static) -> T { + let owner = with_runtime(|runtime| runtime.owner.get()) + .expect("runtime should be alive when created"); + let (value, disposer) = with_runtime(|runtime| { + let prev_observer = runtime.observer.take(); + let prev_owner = runtime.owner.take(); + + runtime.owner.set(owner); + runtime.observer.set(owner); + + let id = runtime.nodes.borrow_mut().insert(ReactiveNode { + value: None, + state: ReactiveNodeState::Clean, + node_type: ReactiveNodeType::Trigger, + }); + runtime.push_scope_property(ScopeProperty::Trigger(id)); + let disposer = Disposer(id); + + runtime.owner.set(Some(id)); + runtime.observer.set(Some(id)); + + let v = f(); + + runtime.observer.set(prev_observer); + runtime.owner.set(prev_owner); + + (v, disposer) + }) + .expect("runtime should be alive when run"); + on_cleanup(move || drop(disposer)); + value +} + impl RuntimeId { /// Removes the runtime, disposing of everything created in it. /// From 2c8f46466b75ea59d92662a1ceb8fdd4f29f333c Mon Sep 17 00:00:00 2001 From: Gabriel de Perthuis Date: Thu, 21 Sep 2023 02:43:20 +0200 Subject: [PATCH 2/2] feat: support default values for annotated server_fn arguments with `#[server(default)]` (#1762) This allows form submission with checkbox inputs to work. For example: let doit = create_server_action::(); #[server(DoItSFn, "/api")] pub async fn doit(#[server(default)] is_good: bool) -> Result<(), ServerFnError> {} If is_good is absent in the request to the server API, `Default::default()` is used instead. --- server_fn_macro/src/lib.rs | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/server_fn_macro/src/lib.rs b/server_fn_macro/src/lib.rs index f40471e165..a066d17367 100644 --- a/server_fn_macro/src/lib.rs +++ b/server_fn_macro/src/lib.rs @@ -84,7 +84,7 @@ pub fn server_macro_impl( let fn_path = fn_path.unwrap_or_else(|| Literal::string("")); let encoding = quote!(#server_fn_path::#encoding); - let body = syn::parse::(body.into())?; + let mut body = syn::parse::(body.into())?; let fn_name = &body.ident; let fn_name_as_str = body.ident.to_string(); let vis = body.vis; @@ -92,7 +92,7 @@ pub fn server_macro_impl( let fields = body .inputs - .iter() + .iter_mut() .filter(|f| { if let Some(ctx) = &server_context { !fn_arg_is_cx(f, ctx) @@ -110,8 +110,33 @@ pub fn server_macro_impl( } FnArg::Typed(t) => t, }; - quote! { pub #typed_arg } - }); + let mut default = false; + let mut other_attrs = Vec::new(); + for attr in typed_arg.attrs.iter() { + if !attr.path().is_ident("server") { + other_attrs.push(attr.clone()); + continue; + } + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("default") && meta.input.is_empty() { + default = true; + Ok(()) + } else { + Err(meta.error( + "Unrecognized #[server] attribute, expected \ + #[server(default)]", + )) + } + })?; + } + typed_arg.attrs = other_attrs; + if default { + Ok(quote! { #[serde(default)] pub #typed_arg }) + } else { + Ok(quote! { pub #typed_arg }) + } + }) + .collect::>>()?; let cx_arg = body.inputs.iter().next().and_then(|f| { server_context