From 158852e5a0afe64deff5270116d4f83da53458b8 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 19 Jun 2023 16:08:46 -0500 Subject: [PATCH] make regestering server functions optional --- docs/guide/examples/server_context.rs | 3 - docs/guide/examples/server_context_state.rs | 3 - docs/guide/examples/server_function.rs | 3 - packages/fullstack/Cargo.toml | 2 +- packages/fullstack/README.md | 1 - .../examples/axum-desktop/src/server.rs | 3 - .../examples/axum-hello-world/src/main.rs | 2 - .../examples/axum-router/src/main.rs | 2 - .../examples/salvo-hello-world/src/main.rs | 2 - .../examples/warp-hello-world/src/main.rs | 2 - packages/fullstack/server-macro/Cargo.toml | 4 +- packages/fullstack/server-macro/src/lib.rs | 2 +- .../fullstack/src/adapters/axum_adapter.rs | 15 ++- .../fullstack/src/adapters/salvo_adapter.rs | 24 ++--- .../fullstack/src/adapters/warp_adapter.rs | 17 ++-- packages/fullstack/src/lib.rs | 2 +- packages/fullstack/src/server_fn.rs | 98 ++++++++++++++----- 17 files changed, 106 insertions(+), 79 deletions(-) diff --git a/docs/guide/examples/server_context.rs b/docs/guide/examples/server_context.rs index 446b0a93a..eb3c5e9c1 100644 --- a/docs/guide/examples/server_context.rs +++ b/docs/guide/examples/server_context.rs @@ -16,9 +16,6 @@ fn main() { use axum::extract::State; use axum::routing::get; - // Register the server function before starting the server - DoubleServer::register().unwrap(); - tokio::runtime::Runtime::new() .unwrap() .block_on(async move { diff --git a/docs/guide/examples/server_context_state.rs b/docs/guide/examples/server_context_state.rs index 390849ff6..c75496561 100644 --- a/docs/guide/examples/server_context_state.rs +++ b/docs/guide/examples/server_context_state.rs @@ -25,9 +25,6 @@ fn main() { use axum::routing::get; use std::sync::Arc; - // Register the server function before starting the server - DoubleServer::register().unwrap(); - tokio::runtime::Runtime::new() .unwrap() .block_on(async move { diff --git a/docs/guide/examples/server_function.rs b/docs/guide/examples/server_function.rs index d803ea60d..55f5104f9 100644 --- a/docs/guide/examples/server_function.rs +++ b/docs/guide/examples/server_function.rs @@ -16,9 +16,6 @@ fn main() { use axum::extract::State; use axum::routing::get; - // Register the server function before starting the server - DoubleServer::register().unwrap(); - tokio::runtime::Runtime::new() .unwrap() .block_on(async move { diff --git a/packages/fullstack/Cargo.toml b/packages/fullstack/Cargo.toml index 5e7da25ca..3a64f8570 100644 --- a/packages/fullstack/Cargo.toml +++ b/packages/fullstack/Cargo.toml @@ -13,7 +13,7 @@ keywords = ["dom", "ui", "gui", "react", "ssr", "fullstack"] [dependencies] # server functions -server_fn = { git = "https://github.com/leptos-rs/leptos", rev = "671b1e4a8fff7a2e05bb621ef08e87be2b18ccae", default-features = false, features = ["stable"] } +server_fn = { git = "https://github.com/leptos-rs/leptos", rev = "15a4e54435eb5a539afb75891292bcccd2cc8e85", default-features = false, features = ["stable"] } dioxus_server_macro = { path = "server-macro" } # warp diff --git a/packages/fullstack/README.md b/packages/fullstack/README.md index ec3d1c833..ecd003ee8 100644 --- a/packages/fullstack/README.md +++ b/packages/fullstack/README.md @@ -46,7 +46,6 @@ fn main() { ); #[cfg(feature = "ssr")] { - GetMeaning::register().unwrap(); tokio::runtime::Runtime::new() .unwrap() .block_on(async move { diff --git a/packages/fullstack/examples/axum-desktop/src/server.rs b/packages/fullstack/examples/axum-desktop/src/server.rs index 7602d1e48..6098ae3f4 100644 --- a/packages/fullstack/examples/axum-desktop/src/server.rs +++ b/packages/fullstack/examples/axum-desktop/src/server.rs @@ -8,9 +8,6 @@ use dioxus_fullstack::prelude::*; #[tokio::main] async fn main() { - PostServerData::register().unwrap(); - GetServerData::register().unwrap(); - let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); axum::Server::bind(&addr) .serve( diff --git a/packages/fullstack/examples/axum-hello-world/src/main.rs b/packages/fullstack/examples/axum-hello-world/src/main.rs index 0301a190c..66c3af096 100644 --- a/packages/fullstack/examples/axum-hello-world/src/main.rs +++ b/packages/fullstack/examples/axum-hello-world/src/main.rs @@ -32,8 +32,6 @@ fn main() { true })); - PostServerData::register().unwrap(); - GetServerData::register().unwrap(); tokio::runtime::Runtime::new() .unwrap() .block_on(async move { diff --git a/packages/fullstack/examples/axum-router/src/main.rs b/packages/fullstack/examples/axum-router/src/main.rs index 96567c791..2b98ad842 100644 --- a/packages/fullstack/examples/axum-router/src/main.rs +++ b/packages/fullstack/examples/axum-router/src/main.rs @@ -34,8 +34,6 @@ fn main() { })); use axum::extract::State; - PostServerData::register().unwrap(); - GetServerData::register().unwrap(); tokio::runtime::Runtime::new() .unwrap() .block_on(async move { diff --git a/packages/fullstack/examples/salvo-hello-world/src/main.rs b/packages/fullstack/examples/salvo-hello-world/src/main.rs index 0cbd3598f..01b67ff99 100644 --- a/packages/fullstack/examples/salvo-hello-world/src/main.rs +++ b/packages/fullstack/examples/salvo-hello-world/src/main.rs @@ -33,8 +33,6 @@ fn main() { })); use salvo::prelude::*; - PostServerData::register().unwrap(); - GetServerData::register().unwrap(); tokio::runtime::Runtime::new() .unwrap() .block_on(async move { diff --git a/packages/fullstack/examples/warp-hello-world/src/main.rs b/packages/fullstack/examples/warp-hello-world/src/main.rs index 5e4c9aecb..34fc24c77 100644 --- a/packages/fullstack/examples/warp-hello-world/src/main.rs +++ b/packages/fullstack/examples/warp-hello-world/src/main.rs @@ -32,8 +32,6 @@ fn main() { true })); - PostServerData::register().unwrap(); - GetServerData::register().unwrap(); tokio::runtime::Runtime::new() .unwrap() .block_on(async move { diff --git a/packages/fullstack/server-macro/Cargo.toml b/packages/fullstack/server-macro/Cargo.toml index 286481bdd..cb039057b 100644 --- a/packages/fullstack/server-macro/Cargo.toml +++ b/packages/fullstack/server-macro/Cargo.toml @@ -7,8 +7,8 @@ edition = "2021" [dependencies] quote = "1.0.26" -server_fn_macro = { git = "https://github.com/leptos-rs/leptos", rev = "671b1e4a8fff7a2e05bb621ef08e87be2b18ccae", features = ["stable"] } -syn = { version = "1", features = ["full"] } +server_fn_macro = { git = "https://github.com/leptos-rs/leptos", rev = "15a4e54435eb5a539afb75891292bcccd2cc8e85", features = ["stable"] } +syn = { version = "2", features = ["full"] } [lib] proc-macro = true diff --git a/packages/fullstack/server-macro/src/lib.rs b/packages/fullstack/server-macro/src/lib.rs index f6df526c1..9a1a2c9c7 100644 --- a/packages/fullstack/server-macro/src/lib.rs +++ b/packages/fullstack/server-macro/src/lib.rs @@ -35,7 +35,6 @@ use server_fn_macro::*; /// ``` /// /// Note the following: -/// - You must **register** the server function by calling `T::register()` somewhere in your main function. /// - **Server functions must be `async`.** Even if the work being done inside the function body /// can run synchronously on the server, from the client’s perspective it involves an asynchronous /// function call. @@ -62,6 +61,7 @@ pub fn server(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream { match server_macro_impl( args.into(), s.into(), + syn::parse_quote!(::dioxus_fullstack::prelude::ServerFnTraitObj), Some(context), Some(syn::parse_quote!(::dioxus_fullstack::prelude::server_fn)), ) { diff --git a/packages/fullstack/src/adapters/axum_adapter.rs b/packages/fullstack/src/adapters/axum_adapter.rs index c634d7f77..1ff136d4d 100644 --- a/packages/fullstack/src/adapters/axum_adapter.rs +++ b/packages/fullstack/src/adapters/axum_adapter.rs @@ -12,7 +12,6 @@ //! dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); //! #[cfg(feature = "ssr")] //! { -//! GetServerData::register().unwrap(); //! tokio::runtime::Runtime::new() //! .unwrap() //! .block_on(async move { @@ -107,7 +106,7 @@ pub trait DioxusRouterExt { fn register_server_fns_with_handler( self, server_fn_route: &'static str, - handler: impl Fn(ServerFunction) -> H, + handler: impl FnMut(server_fn::ServerFnTraitObj) -> H, ) -> Self where H: Handler, @@ -232,7 +231,7 @@ where fn register_server_fns_with_handler( self, server_fn_route: &'static str, - mut handler: impl FnMut(ServerFunction) -> H, + mut handler: impl FnMut(server_fn::ServerFnTraitObj) -> H, ) -> Self where H: Handler, @@ -243,7 +242,7 @@ where for server_fn_path in DioxusServerFnRegistry::paths_registered() { let func = DioxusServerFnRegistry::get(server_fn_path).unwrap(); let full_route = format!("{server_fn_route}/{server_fn_path}"); - match func.encoding { + match func.encoding() { Encoding::Url | Encoding::Cbor => { router = router.route(&full_route, post(handler(func))); } @@ -314,7 +313,7 @@ where let cfg = cfg.into(); // Add server functions and render index.html - self.serve_static_assets(&cfg.assets_path) + self.serve_static_assets(cfg.assets_path) .route( "/", get(render_handler).with_state((cfg, SSRState::default())), @@ -371,7 +370,7 @@ async fn render_handler( /// A default handler for server functions. It will deserialize the request, call the server function, and serialize the response. pub async fn server_fn_handler( server_context: DioxusServerContext, - function: ServerFunction, + function: server_fn::ServerFnTraitObj, parts: Arc, body: Body, ) -> impl IntoResponse { @@ -389,11 +388,11 @@ pub async fn server_fn_handler( .expect("couldn't spawn runtime") .block_on(async { let query = &query_string.into(); - let data = match &function.encoding { + let data = match &function.encoding() { Encoding::Url | Encoding::Cbor => &body, Encoding::GetJSON | Encoding::GetCBOR => query, }; - let resp = match (function.trait_obj)(server_context.clone(), &data).await { + let resp = match function.call(server_context.clone(), data).await { Ok(serialized) => { // if this is Accept: application/json then send a serialized JSON response let accept_header = parts diff --git a/packages/fullstack/src/adapters/salvo_adapter.rs b/packages/fullstack/src/adapters/salvo_adapter.rs index 94814fea0..c2968ff75 100644 --- a/packages/fullstack/src/adapters/salvo_adapter.rs +++ b/packages/fullstack/src/adapters/salvo_adapter.rs @@ -12,7 +12,6 @@ //! #[cfg(feature = "ssr")] //! { //! use salvo::prelude::*; -//! GetServerData::register().unwrap(); //! tokio::runtime::Runtime::new() //! .unwrap() //! .block_on(async move { @@ -110,7 +109,7 @@ pub trait DioxusRouterExt { fn register_server_fns_with_handler( self, server_fn_route: &'static str, - handler: impl Fn(ServerFunction) -> H, + handler: impl Fn(server_fn::ServerFnTraitObj) -> H, ) -> Self where H: Handler + 'static; @@ -204,7 +203,7 @@ impl DioxusRouterExt for Router { fn register_server_fns_with_handler( self, server_fn_route: &'static str, - mut handler: impl FnMut(ServerFunction) -> H, + mut handler: impl FnMut(server_fn::ServerFnTraitObj) -> H, ) -> Self where H: Handler + 'static, @@ -213,7 +212,7 @@ impl DioxusRouterExt for Router { for server_fn_path in DioxusServerFnRegistry::paths_registered() { let func = DioxusServerFnRegistry::get(server_fn_path).unwrap(); let full_route = format!("{server_fn_route}/{server_fn_path}"); - match func.encoding { + match func.encoding() { Encoding::Url | Encoding::Cbor => { router = router.push(Router::with_path(&full_route).post(handler(func))); } @@ -280,7 +279,7 @@ impl DioxusRouterExt for Router { ) -> Self { let cfg = cfg.into(); - self.serve_static_assets(&cfg.assets_path) + self.serve_static_assets(cfg.assets_path) .connect_hot_reload() .register_server_fns(server_fn_path) .push(Router::with_path("/").get(SSRHandler { cfg })) @@ -288,8 +287,8 @@ impl DioxusRouterExt for Router { fn connect_hot_reload(self) -> Self { let mut _dioxus_router = Router::with_path("_dioxus"); - _dioxus_router = _dioxus_router - .push(Router::with_path("hot_reload").handle(HotReloadHandler::default())); + _dioxus_router = + _dioxus_router.push(Router::with_path("hot_reload").handle(HotReloadHandler)); #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] { _dioxus_router = _dioxus_router.push(Router::with_path("disconnect").handle(ignore_ws)); @@ -346,12 +345,15 @@ impl Handler for SSRHandler /// A default handler for server functions. It will deserialize the request body, call the server function, and serialize the response. pub struct ServerFnHandler { server_context: DioxusServerContext, - function: ServerFunction, + function: server_fn::ServerFnTraitObj, } impl ServerFnHandler { /// Create a new server function handler with the given server context and server function. - pub fn new(server_context: impl Into, function: ServerFunction) -> Self { + pub fn new( + server_context: impl Into, + function: server_fn::ServerFnTraitObj, + ) -> Self { let server_context = server_context.into(); Self { server_context, @@ -395,11 +397,11 @@ impl ServerFnHandler { tokio::runtime::Runtime::new() .expect("couldn't spawn runtime") .block_on(async move { - let data = match &function.encoding { + let data = match function.encoding() { Encoding::Url | Encoding::Cbor => &body, Encoding::GetJSON | Encoding::GetCBOR => &query, }; - let resp = (function.trait_obj)(server_context, data).await; + let resp = function.call(server_context, data).await; resp_tx.send(resp).unwrap(); }) diff --git a/packages/fullstack/src/adapters/warp_adapter.rs b/packages/fullstack/src/adapters/warp_adapter.rs index 872a94cdd..b9f23ae7f 100644 --- a/packages/fullstack/src/adapters/warp_adapter.rs +++ b/packages/fullstack/src/adapters/warp_adapter.rs @@ -11,7 +11,6 @@ //! dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); //! #[cfg(feature = "ssr")] //! { -//! GetServerData::register().unwrap(); //! tokio::runtime::Runtime::new() //! .unwrap() //! .block_on(async move { @@ -93,7 +92,7 @@ pub fn register_server_fns_with_handler( mut handler: H, ) -> BoxedFilter<(R,)> where - H: FnMut(String, ServerFunction) -> F, + H: FnMut(String, server_fn::ServerFnTraitObj) -> F, F: Filter + Send + Sync + 'static, F::Extract: Send, R: Reply + 'static, @@ -104,7 +103,7 @@ where let full_route = format!("{server_fn_route}/{server_fn_path}") .trim_start_matches('/') .to_string(); - let route = handler(full_route, func.clone()).boxed(); + let route = handler(full_route, func).boxed(); if let Some(boxed_filter) = filter.take() { filter = Some(boxed_filter.or(route).unify().boxed()); } else { @@ -128,7 +127,7 @@ where /// ``` pub fn register_server_fns(server_fn_route: &'static str) -> BoxedFilter<(impl Reply,)> { register_server_fns_with_handler(server_fn_route, |full_route, func| { - path(full_route.clone()) + path(full_route) .and(warp::post().or(warp::get()).unify()) .and(request_parts()) .and(warp::body::bytes()) @@ -250,7 +249,7 @@ impl warp::reject::Reject for RecieveFailed {} /// A default handler for server functions. It will deserialize the request body, call the server function, and serialize the response. pub async fn server_fn_handler( server_context: impl Into, - function: ServerFunction, + function: server_fn::ServerFnTraitObj, parts: RequestParts, body: Bytes, ) -> Result, warp::Rejection> { @@ -274,11 +273,11 @@ pub async fn server_fn_handler( .as_bytes() .to_vec() .into(); - let data = match &function.encoding { + let data = match function.encoding() { Encoding::Url | Encoding::Cbor => &body, Encoding::GetJSON | Encoding::GetCBOR => &query, }; - let resp = match (function.trait_obj)(server_context.clone(), data).await { + let resp = match function.call(server_context.clone(), data).await { Ok(serialized) => { // if this is Accept: application/json then send a serialized JSON response let accept_header = parts @@ -373,7 +372,7 @@ pub fn connect_hot_reload() -> impl Filter impl Filter); impl Drop for DisconnectOnDrop { fn drop(&mut self) { - let _ = self.0.take().unwrap().close(); + std::mem::drop(self.0.take().unwrap().close()); } } diff --git a/packages/fullstack/src/lib.rs b/packages/fullstack/src/lib.rs index be437bc53..18cde9adb 100644 --- a/packages/fullstack/src/lib.rs +++ b/packages/fullstack/src/lib.rs @@ -34,7 +34,7 @@ pub mod prelude { #[cfg(feature = "ssr")] pub use crate::server_context::RequestParts; pub use crate::server_context::{DioxusServerContext, HasServerContext}; - pub use crate::server_fn::ServerFn; + pub use crate::server_fn::DioxusServerFn; #[cfg(feature = "ssr")] pub use crate::server_fn::{ServerFnTraitObj, ServerFunction}; pub use dioxus_server_macro::*; diff --git a/packages/fullstack/src/server_fn.rs b/packages/fullstack/src/server_fn.rs index da7311263..231285149 100644 --- a/packages/fullstack/src/server_fn.rs +++ b/packages/fullstack/src/server_fn.rs @@ -1,30 +1,76 @@ use crate::server_context::DioxusServerContext; #[cfg(any(feature = "ssr", doc))] +#[derive(Clone)] /// A trait object for a function that be called on serializable arguments and returns a serializable result. -pub type ServerFnTraitObj = server_fn::ServerFnTraitObj; +pub struct ServerFnTraitObj(server_fn::ServerFnTraitObj); + +#[cfg(any(feature = "ssr", doc))] +impl std::ops::Deref for ServerFnTraitObj { + type Target = server_fn::ServerFnTraitObj; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[cfg(any(feature = "ssr", doc))] +impl std::ops::DerefMut for ServerFnTraitObj { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +#[cfg(any(feature = "ssr", doc))] +impl ServerFnTraitObj { + fn new( + prefix: &'static str, + url: &'static str, + encoding: server_fn::Encoding, + run: ServerFunction, + ) -> Self { + Self(server_fn::ServerFnTraitObj::new(prefix, url, encoding, run)) + } + + /// Create a new `ServerFnTraitObj` from a `server_fn::ServerFnTraitObj`. + pub const fn from_generic_server_fn( + server_fn: server_fn::ServerFnTraitObj, + ) -> Self { + Self(server_fn) + } +} + +#[cfg(any(feature = "ssr", doc))] +server_fn::inventory::collect!(ServerFnTraitObj); #[cfg(any(feature = "ssr", doc))] /// A server function that can be called on serializable arguments and returns a serializable result. -pub type ServerFunction = server_fn::ServerFunction; +pub type ServerFunction = server_fn::SerializedFnTraitObj; #[cfg(any(feature = "ssr", doc))] #[allow(clippy::type_complexity)] static REGISTERED_SERVER_FUNCTIONS: once_cell::sync::Lazy< - std::sync::Arc>>, -> = once_cell::sync::Lazy::new(Default::default); + std::sync::Arc>>, +> = once_cell::sync::Lazy::new(|| { + let mut map = std::collections::HashMap::new(); + for server_fn in server_fn::inventory::iter:: { + map.insert(server_fn.0.url(), server_fn.clone()); + } + std::sync::Arc::new(std::sync::RwLock::new(map)) +}); #[cfg(any(feature = "ssr", doc))] /// The registry of all Dioxus server functions. pub struct DioxusServerFnRegistry; -#[cfg(any(feature = "ssr"))] +#[cfg(feature = "ssr")] impl server_fn::ServerFunctionRegistry for DioxusServerFnRegistry { type Error = ServerRegistrationFnError; - fn register( + fn register_explicit( + prefix: &'static str, url: &'static str, - server_function: std::sync::Arc, + server_function: ServerFunction, encoding: server_fn::Encoding, ) -> Result<(), Self::Error> { // store it in the hashmap @@ -33,10 +79,7 @@ impl server_fn::ServerFunctionRegistry for DioxusServerFnRe .map_err(|e| ServerRegistrationFnError::Poisoned(e.to_string()))?; let prev = write.insert( url, - ServerFunction { - trait_obj: server_function, - encoding, - }, + ServerFnTraitObj::new(prefix, url, encoding, server_function), ); // if there was already a server function with this key, @@ -53,27 +96,32 @@ impl server_fn::ServerFunctionRegistry for DioxusServerFnRe } } - /// Returns the server function registered at the given URL, or `None` if no function is registered at that URL. - fn get(url: &str) -> Option { - REGISTERED_SERVER_FUNCTIONS - .read() - .ok() - .and_then(|fns| fns.get(url).cloned()) + fn register( + url: &'static str, + server_function: ServerFunction, + encoding: server_fn::Encoding, + ) -> Result<(), Self::Error> { + Self::register_explicit("", url, server_function, encoding) } /// Returns the server function registered at the given URL, or `None` if no function is registered at that URL. - fn get_trait_obj(url: &str) -> Option> { + fn get(url: &str) -> Option> { REGISTERED_SERVER_FUNCTIONS .read() .ok() - .and_then(|fns| fns.get(url).map(|f| f.trait_obj.clone())) + .and_then(|fns| fns.get(url).map(|inner| inner.0.clone())) + } + + /// Returns the server function registered at the given URL, or `None` if no function is registered at that URL. + fn get_trait_obj(url: &str) -> Option> { + Self::get(url) } fn get_encoding(url: &str) -> Option { REGISTERED_SERVER_FUNCTIONS .read() .ok() - .and_then(|fns| fns.get(url).map(|f| f.encoding.clone())) + .and_then(|fns| fns.get(url).map(|f| f.encoding())) } /// Returns a list of all registered server functions. @@ -103,16 +151,16 @@ pub enum ServerRegistrationFnError { /// /// Server functions are created using the `server` macro. /// -/// The function should be registered by calling `ServerFn::register()`. The set of server functions +/// The set of server functions /// can be queried on the server for routing purposes by calling [server_fn::ServerFunctionRegistry::get]. /// /// Technically, the trait is implemented on a type that describes the server function's arguments, not the function itself. -pub trait ServerFn: server_fn::ServerFn { +pub trait DioxusServerFn: server_fn::ServerFn { /// Registers the server function, allowing the client to query it by URL. #[cfg(any(feature = "ssr", doc))] - fn register() -> Result<(), server_fn::ServerFnError> { - Self::register_in::() + fn register_explicit() -> Result<(), server_fn::ServerFnError> { + Self::register_in_explicit::() } } -impl ServerFn for T where T: server_fn::ServerFn {} +impl DioxusServerFn for T where T: server_fn::ServerFn {}