"This example demonstrates creating a generic function that takes any type that implements display that we've registered."
-
- Result 1
+
+
+
+ Result 1
+
+ {
+ move || string_to_string.get()
+ }
+
+ Result 2
+
+ {
+ move || u8_to_string.get()
+ }
+
+
+
+
+
+ }
+}
+
+pub trait SomeTrait {
+ fn some_method() -> String;
+}
+
+pub struct AStruct;
+pub struct AStruct2;
+impl SomeTrait for AStruct {
+ fn some_method() -> String {
+ String::from("Hello world...")
+ }
+}
+impl SomeTrait for AStruct2 {
+ fn some_method() -> String {
+ String::from("HELLO WORLD")
+ }
+}
+
+#[server]
+#[register(,)]
+pub async fn server_hello_world_generic(
+) -> Result {
+ Ok(S::some_method())
+}
+
+#[component]
+pub fn GenericHelloWorld() -> impl IntoView {
+ let s = RwSignal::new(String::new());
+ Effect::new(move |_| {
+ spawn_local(async move {
+ match server_hello_world_generic::().await {
+ Ok(hello) => s.set(hello),
+ Err(err) => leptos::logging::log!("{err:?}"),
+ }
+ })
+ });
+ let s2 = RwSignal::new(String::new());
+ Effect::new(move |_| {
+ spawn_local(async move {
+ match server_hello_world_generic::().await {
+ Ok(hello) => s2.set(hello),
+ Err(err) => leptos::logging::log!("{err:?}"),
+ }
+ })
+ });
+ view! {
+ Using generic functions without generic inputs
+ "This example demonstrates creating a generic server function that doesn't take any generic input."
+ {"Results"}
+ { move || format!("With generic specified to {} we get {} from the server", std::any::type_name::(), s.get())}
+ { move || format!("With generic specified to {} we get {} from the server", std::any::type_name::(), s2.get())}
+
+ }
+}
+
+#[derive(Clone)]
+struct SomeDefault;
+impl SomeTrait for SomeDefault {
+ fn some_method() -> String {
+ String::from("just a default hello world...")
+ }
+}
+
+#[server]
+#[register()]
+pub async fn server_hello_world_generic_with_default<
+ S: SomeTrait = SomeDefault,
+>() -> Result {
+ Ok(S::some_method())
+}
+
+#[component]
+pub fn GenericHelloWorldWithDefaults() -> impl IntoView {
+ let action = ServerAction::::new();
+ Effect::new(move |_| {
+ action.dispatch(ServerHelloWorldGenericWithDefault {
+ _marker: std::marker::PhantomData,
+ });
+ });
+
+ view! {
+ Using generic functions without generic inputs but a specified default type
+ "This example demonstrates creating a generic server function that doesn't take any generic input and has a default generic type."
+ {"Results"}
+ { move || action.value_local().read_only().get().map(|s|s.map(|s|format!("With default generic we get {s} from the server", )))}
+ }
+}
+
+#[cfg(feature = "ssr")]
+pub struct ServerOnlyStruct;
+#[cfg(feature = "ssr")]
+pub struct SsrOnlyStructButDifferent;
+#[cfg(feature = "ssr")]
+pub trait ServerOnlyTrait {
+ fn some_method() -> String {
+ String::from("I'm a backend!")
+ }
+}
+
+#[cfg(feature = "ssr")]
+impl ServerOnlyTrait for ServerOnlyStruct {}
+#[cfg(feature = "ssr")]
+impl ServerOnlyTrait for SsrOnlyStructButDifferent {
+ fn some_method() -> String {
+ String::from("I'm a different backend!")
+ }
+}
+server_fn::ssr_type_shim! {ServerOnlyStruct, SsrOnlyStructButDifferent}
+server_fn::ssr_trait_shim! {ServerOnlyTrait}
+server_fn::ssr_impl_shim! {ServerOnlyStruct:ServerOnlyTrait,SsrOnlyStructButDifferent:ServerOnlyTrait}
+
+#[server]
+#[register(,
+
+)]
+pub async fn generic_server_with_ssr_only_types(
+) -> Result {
+ Ok(T::some_method())
+}
+
+#[component]
+pub fn GenericSsrOnlyTypes() -> impl IntoView {
+ let s = RwSignal::new(String::new());
+ // spawn on the client
+ Effect::new(move |_| {
+ spawn_local(async move {
+ match generic_server_with_ssr_only_types::(
+ )
+ .await
{
- move || string_to_string.get().map(|r| r.map(|r|format!("{r}")))
+ Ok(hello) => s.set(hello),
+ Err(err) => leptos::logging::log!("{err:?}"),
}
-
- Result 2
+ });
+ });
+
+ let s2 = RwSignal::new(String::new());
+ // spawn on the client
+ Effect::new(move |_| {
+ spawn_local(async move {
+ match generic_server_with_ssr_only_types::<
+ SsrOnlyStructButDifferentPhantom,
+ >()
+ .await
{
- move || u8_to_string.get().map(|r| r.map(|r|format!("{r}")))
+ Ok(hello) => s2.set(hello),
+ Err(err) => leptos::logging::log!("{err:?}"),
}
-
+ });
+ });
+ view! {
+ Using generic functions with a type that only exists on the server.
+ "This example demonstrates how to make use of the helper macros and phantom types to make your backend generic and specifiable from your frontend."
+ {"Results"}
+ { move || format!("With backend 1 we get {} from the server", s.get())}
+ { move || format!("With backend 2 we get {} from the server", s2.get())}
-
}
}
diff --git a/server_fn/src/lib.rs b/server_fn/src/lib.rs
index e2ffc7c747..b139015743 100644
--- a/server_fn/src/lib.rs
+++ b/server_fn/src/lib.rs
@@ -117,7 +117,6 @@ pub mod response;
/// Helpers for creating isomorphic generic code.
#[cfg(feature = "ssr_generics")]
pub mod ssr_generics;
-
#[cfg(feature = "actix")]
#[doc(hidden)]
pub use ::actix_web as actix_export;
@@ -130,6 +129,9 @@ pub use ::bytes as bytes_export;
#[cfg(feature = "generic")]
#[doc(hidden)]
pub use ::http as http_export;
+#[cfg(feature = "ssr_generics")]
+#[doc(hidden)]
+pub use ::paste as paste_export;
use client::Client;
use codec::{Encoding, FromReq, FromRes, IntoReq, IntoRes};
#[doc(hidden)]
diff --git a/server_fn/src/ssr_generics.rs b/server_fn/src/ssr_generics.rs
index 375e493917..d521d176f8 100644
--- a/server_fn/src/ssr_generics.rs
+++ b/server_fn/src/ssr_generics.rs
@@ -61,17 +61,18 @@ pub trait ServerType {
/// ```
///
/// It also implements a hidden trait ServerType under an ssr feature flag whose associated type is the original server only type.
+#[macro_export]
macro_rules! ssr_type_shim{
($($type_name:ident),*) => {
$(
- paste::paste!{
+ $crate::paste_export::paste!{
/// An isomorphic marker type for $type_name
pub struct [<$type_name Phantom>];
}
)*
$(
#[cfg(feature="ssr")]
- paste::paste! { impl ServerType for [<$type_name Phantom>] {
+ $crate::paste_export::paste! { impl $crate::ssr_generics::ServerType for [<$type_name Phantom>] {
type ServerType = $type_name;
}
}
@@ -87,11 +88,11 @@ macro_rules! ssr_type_shim{
/// // Will generate code
/// // pub trait SpecificTraitConstraint{}
/// ```
-///
+#[macro_export]
macro_rules! ssr_trait_shim{
($($trait_name:ident),*) => {
$(
- paste::paste! {
+ $crate::paste_export::paste! {
/// An empty isomorphic trait to mirror $trait_name
pub trait [<$trait_name Constraint>] {}
}
@@ -105,12 +106,13 @@ macro_rules! ssr_trait_shim{
/// // uses traditional + syntax for additonal traits past 1 like in normal trait bounds.
/// ssr_impl_shim!(BackendType:BackendTrait, BackendType2:BackendTrait + BackendTrait2);
/// ```
+#[macro_export]
macro_rules! ssr_impl_shim{
($($type_name:ident : $trait_name:ident $(+ $trait_name_tail:ident)*),*) => {
$(
- paste:: paste! { impl [<$trait_name Constraint>] for [<$type_name Phantom>] {} }
+ $crate::paste_export:: paste! { impl [<$trait_name Constraint>] for [<$type_name Phantom>] {} }
$(
- paste:: paste! { impl [<$trait_name_tail Constraint>] for [<$type_name Phantom>] {} }
+ ::server_fn::paste_export:: paste! { impl [<$trait_name_tail Constraint>] for [<$type_name Phantom>] {} }
)*
)*
}
diff --git a/server_fn_macro/src/lib.rs b/server_fn_macro/src/lib.rs
index 7a4be9af1f..50fc1d82fc 100644
--- a/server_fn_macro/src/lib.rs
+++ b/server_fn_macro/src/lib.rs
@@ -59,13 +59,44 @@ fn extract_register(body: &mut ServerFnBody) -> Result