Simplify the launch builder (#2967)

* improve documentation for the fullstack server context

* Add a section about axum integration to the crate root docs

* make serve_dioxus_application accept the cfg builder directly

* remove unused server_fn module

* improve fullstack config docs

* improve documentation for the server function macro

* fix axum router extension link

* Fix doc tests

* Fix launch builder

* Simplify the launch builder

* don't re-export launch in the prelude

* refactor fullstack launch

* Fix fullstack launch builder

* Update static generation with the new builder api

* fix some formatting/overly broad launch replacements

* fix custom menu example

* fix fullstack/static generation examples

* Fix static generation launch

* A few small formatting fixes

* Fix a few doc tests

* implement LaunchConfig for serve configs

* fix fullstack launch with separate web and server launch methods

* fix check with all features

* dont expose inner core module

* clippy and check

* fix readme

---------

Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
This commit is contained in:
Evan AlmloffandJonathan Kelley authored and GitHub committed 2024-10-10 16:00:58 -07:00
1 parent 2d7bef44e8
commit 20d146d9bd
126 files changed
+618 -989

No files matched your search

+72 -2
View File
@@ -37,7 +37,77 @@ Full stack Dioxus in under 30 lines of code
use dioxus::prelude::*;
fn main() {
launch(App);
dioxus::launch(App);
}
#[component]
fn App() -> Element {
let mut meaning = use_signal(|| None);
rsx! {
h1 { "Meaning of life: {meaning:?}" }
button {
onclick: move |_| async move {
if let Ok(data) = get_meaning("life the universe and everything".into()).await {
meaning.set(data);
}
},
"Run a server function"
}
}
}
#[server]
async fn get_meaning(of: String) -> Result<Option<u32>, ServerFnError> {
Ok(of.contains("life").then(|| 42))
}
```
## Axum Integration
If you have an existing Axum router or you need more control over the server, you can use the [`DioxusRouterExt`](https://docs.rs/dioxus-fullstack/0.6.0-alpha.2/dioxus_fullstack/prelude/trait.DioxusRouterExt.html) trait to integrate with your existing Axum router.
First, make sure your `axum` dependency is optional and enabled by the server feature flag. Axum cannot be compiled to wasm, so if it is enabled by default, it will cause a compile error:
```toml
[dependencies]
dioxus = { version = "*", features = ["fullstack"] }
axum = { version = "0.7.0", optional = true }
[features]
server = ["dep:axum", "dioxus/server"]
web = ["dioxus/web"]
```
Then we can set up dioxus with the axum server:
```rust, no_run
#![allow(non_snake_case)]
use dioxus::prelude::*;
// The entry point for the server
#[cfg(feature = "server")]
fn main() {
// Get the address the server should run on. If the CLI is running, the CLI proxies fullstack into the main address
// and we use the generated address the CLI gives us
let address = dioxus_cli_config::RuntimeCLIArguments::fullstack_address_or_localhost();
// Set up the axum router
let router = axum::Router::new()
// You can add a dioxus application to the router with the `serve_dioxus_application` method
// This will add a fallback route to the router that will serve your component and server functions
.serve_dioxus_application(ServeConfigBuilder::default(), App);
// Finally, we can launch the server
let router = router.into_make_service();
let listener = tokio::net::TcpListener::bind(address).await.unwrap();
axum::serve(listener, router).await.unwrap();
}
// For any other platform, we just launch the app
#[cfg(not(feature = "server"))]
fn main() {
dioxus::launch(App);
}
#[component]
@@ -111,7 +181,7 @@ fn main() {
// For any other platform, we just launch the app
#[cfg(not(feature = "server"))]
fn main() {
launch(App);
dioxus::launch(App);
}
#[component]
-289
View File
@@ -1,289 +0,0 @@
//! Launch helper macros for fullstack apps
#![allow(unused)]
use crate::prelude::*;
use dioxus_lib::prelude::*;
use std::sync::Arc;
/// Settings for a fullstack app.
///
/// Depending on what features are enabled, you can pass in configurations for each client platform as well as the server:
/// ```rust, no_run
/// # fn app() -> Element { todo!() }
/// use dioxus::prelude::*;
///
/// let mut cfg = dioxus::fullstack::Config::new();
///
/// // Only set the server config if the server feature is enabled
/// server_only! {
/// cfg = cfg.with_server_config(ServeConfigBuilder::default());
/// }
///
/// // Only set the web config if the web feature is enabled
/// web! {
/// cfg = cfg.with_web_config(dioxus::web::Config::default());
/// }
///
/// // Only set the desktop config if the desktop feature is enabled
/// desktop! {
/// cfg = cfg.with_desktop_config(dioxus::desktop::Config::default());
/// }
///
/// // Finally, launch the app with the config
/// LaunchBuilder::new()
/// .with_cfg(cfg)
/// .launch(app);
/// ```
pub struct Config {
#[cfg(feature = "server")]
pub(crate) server_cfg: ServeConfigBuilder,
#[cfg(feature = "web")]
pub(crate) web_cfg: dioxus_web::Config,
#[cfg(feature = "desktop")]
pub(crate) desktop_cfg: dioxus_desktop::Config,
#[cfg(feature = "mobile")]
pub(crate) mobile_cfg: dioxus_mobile::Config,
}
#[allow(clippy::derivable_impls)]
impl Default for Config {
fn default() -> Self {
Self {
#[cfg(feature = "server")]
server_cfg: ServeConfigBuilder::new(),
#[cfg(feature = "web")]
web_cfg: dioxus_web::Config::default(),
#[cfg(feature = "desktop")]
desktop_cfg: dioxus_desktop::Config::default(),
#[cfg(feature = "mobile")]
mobile_cfg: dioxus_mobile::Config::default(),
}
}
}
impl Config {
/// Create a new config for a fullstack app.
pub fn new() -> Self {
Self::default()
}
/// Set the incremental renderer config. The incremental config can be used to improve
/// performance of heavy routes by caching the rendered html in memory and/or the file system.
///
/// ```rust, no_run
/// # fn app() -> Element { todo!() }
/// use dioxus::prelude::*;
///
/// let mut cfg = dioxus::fullstack::Config::new();
///
/// // Only set the server config if the server feature is enabled
/// server_only! {
/// cfg = cfg.incremental(IncrementalRendererConfig::default().with_memory_cache_limit(10000));
/// }
///
/// // Finally, launch the app with the config
/// LaunchBuilder::new()
/// .with_cfg(cfg)
/// .launch(app);
/// ```
#[cfg(feature = "server")]
#[cfg_attr(docsrs, doc(cfg(feature = "server")))]
pub fn incremental(self, cfg: IncrementalRendererConfig) -> Self {
Self {
server_cfg: self.server_cfg.incremental(cfg),
..self
}
}
/// Set the server config
/// ```rust, no_run
/// # fn app() -> Element { todo!() }
/// use dioxus::prelude::*;
///
/// let mut cfg = dioxus::fullstack::Config::new();
///
/// // Only set the server config if the server feature is enabled
/// server_only! {
/// cfg = cfg.with_server_config(ServeConfigBuilder::default());
/// }
///
/// // Finally, launch the app with the config
/// LaunchBuilder::new()
/// .with_cfg(cfg)
/// .launch(app);
/// ```
#[cfg(feature = "server")]
#[cfg_attr(docsrs, doc(cfg(feature = "server")))]
pub fn with_server_config(self, server_cfg: ServeConfigBuilder) -> Self {
Self { server_cfg, ..self }
}
/// Set the server config by modifying the config in place
///
/// ```rust, no_run
/// # fn app() -> Element { todo!() }
/// use dioxus::prelude::*;
///
/// let mut cfg = dioxus::fullstack::Config::new();
///
/// // Only set the server config if the server feature is enabled
/// server_only! {
/// cfg.set_server_config(ServeConfigBuilder::default());
/// }
///
/// // Finally, launch the app with the config
/// LaunchBuilder::new()
/// .with_cfg(cfg)
/// .launch(app);
/// ```
#[cfg(feature = "server")]
#[cfg_attr(docsrs, doc(cfg(feature = "server")))]
pub fn set_server_config(&mut self, server_cfg: ServeConfigBuilder) {
self.server_cfg = server_cfg;
}
/// Set the web config
/// ```rust, no_run
/// # fn app() -> Element { todo!() }
/// use dioxus::prelude::*;
///
/// let mut cfg = dioxus::fullstack::Config::new();
///
/// // Only set the web config if the server feature is enabled
/// web! {
/// cfg = cfg.with_web_config(dioxus::web::Config::default());
/// }
///
/// // Finally, launch the app with the config
/// LaunchBuilder::new()
/// .with_cfg(cfg)
/// .launch(app);
/// ```
#[cfg(feature = "web")]
#[cfg_attr(docsrs, doc(cfg(feature = "web")))]
pub fn with_web_config(self, web_cfg: dioxus_web::Config) -> Self {
Self { web_cfg, ..self }
}
/// Set the server config by modifying the config in place
///
/// ```rust, no_run
/// # fn app() -> Element { todo!() }
/// use dioxus::prelude::*;
///
/// let mut cfg = dioxus::fullstack::Config::new();
///
/// // Only set the web config if the server feature is enabled
/// web! {
/// cfg.set_web_config(dioxus::web::Config::default());
/// }
///
/// // Finally, launch the app with the config
/// LaunchBuilder::new()
/// .with_cfg(cfg)
/// .launch(app);
/// ```
#[cfg(feature = "web")]
#[cfg_attr(docsrs, doc(cfg(feature = "web")))]
pub fn set_web_config(&mut self, web_cfg: dioxus_web::Config) {
self.web_cfg = web_cfg;
}
/// Set the desktop config
/// ```rust, no_run
/// # fn app() -> Element { todo!() }
/// use dioxus::prelude::*;
///
/// let mut cfg = dioxus::fullstack::Config::new();
///
/// // Only set the desktop config if the server feature is enabled
/// desktop! {
/// cfg = cfg.with_desktop_config(dioxus::desktop::Config::default());
/// }
///
/// // Finally, launch the app with the config
/// LaunchBuilder::new()
/// .with_cfg(cfg)
/// .launch(app);
/// ```
#[cfg(feature = "desktop")]
#[cfg_attr(docsrs, doc(cfg(feature = "desktop")))]
pub fn with_desktop_config(self, desktop_cfg: dioxus_desktop::Config) -> Self {
Self {
desktop_cfg,
..self
}
}
/// Set the desktop config by modifying the config in place
///
/// ```rust, no_run
/// # fn app() -> Element { todo!() }
/// use dioxus::prelude::*;
///
/// let mut cfg = dioxus::fullstack::Config::new();
///
/// // Only set the desktop config if the server feature is enabled
/// desktop! {
/// cfg.set_desktop_config(dioxus::desktop::Config::default());
/// }
///
/// // Finally, launch the app with the config
/// LaunchBuilder::new()
/// .with_cfg(cfg)
/// .launch(app);
/// ```
#[cfg(feature = "desktop")]
#[cfg_attr(docsrs, doc(cfg(feature = "desktop")))]
pub fn set_desktop_config(&mut self, desktop_cfg: dioxus_desktop::Config) {
self.desktop_cfg = desktop_cfg;
}
/// Set the mobile config
/// ```rust, no_run
/// # fn app() -> Element { todo!() }
/// use dioxus::prelude::*;
///
/// let mut cfg = dioxus::fullstack::Config::new();
///
/// // Only set the mobile config if the server feature is enabled
/// mobile! {
/// cfg = cfg.with_mobile_cfg(dioxus::mobile::Config::default());
/// }
///
/// // Finally, launch the app with the config
/// LaunchBuilder::new()
/// .with_cfg(cfg)
/// .launch(app);
/// Set the mobile config.
#[cfg(feature = "mobile")]
#[cfg_attr(docsrs, doc(cfg(feature = "mobile")))]
pub fn with_mobile_cfg(self, mobile_cfg: dioxus_mobile::Config) -> Self {
Self { mobile_cfg, ..self }
}
/// Set the mobile config by modifying the config in place
/// ```rust, no_run
/// # fn app() -> Element { todo!() }
/// use dioxus::prelude::*;
///
/// let mut cfg = dioxus::fullstack::Config::new();
///
/// // Only set the mobile config if the server feature is enabled
/// mobile! {
/// cfg.set_mobile_cfg(dioxus::mobile::Config::default());
/// }
///
/// // Finally, launch the app with the config
/// LaunchBuilder::new()
/// .with_cfg(cfg)
/// .launch(app);
/// Set the mobile config.
#[cfg(feature = "mobile")]
#[cfg_attr(docsrs, doc(cfg(feature = "mobile")))]
pub fn set_mobile_cfg(&mut self, mobile_cfg: dioxus_mobile::Config) {
self.mobile_cfg = mobile_cfg;
}
}
+2 -2
View File
@@ -1,8 +1,8 @@
//! This module contains the document providers for the fullstack platform.
#[cfg(feature = "server")]
pub(crate) mod server;
pub mod server;
#[cfg(feature = "server")]
pub use server::ServerDocument;
#[cfg(all(feature = "web", feature = "document"))]
pub(crate) mod web;
pub mod web;
+2 -1
View File
@@ -11,7 +11,8 @@ fn head_element_written_on_server() -> bool {
.unwrap_or_default()
}
pub(crate) struct FullstackWebDocument;
/// A document provider for fullstack web clients
pub struct FullstackWebDocument;
impl Document for FullstackWebDocument {
fn new_evaluator(
-171
View File
@@ -1,171 +0,0 @@
//! This module contains the `launch` function, which is the main entry point for dioxus fullstack
use std::{any::Any, sync::Arc};
use dioxus_lib::prelude::{Element, VirtualDom};
pub use crate::Config;
#[allow(unused)]
pub(crate) type ContextProviders = Arc<
Vec<Box<dyn Fn() -> Box<dyn std::any::Any + Send + Sync + 'static> + Send + Sync + 'static>>,
>;
#[allow(unused)]
fn virtual_dom_factory(
root: fn() -> Element,
contexts: ContextProviders,
) -> impl Fn() -> VirtualDom + 'static {
move || {
let mut vdom = VirtualDom::new(root);
for context in &*contexts {
vdom.insert_any_root_context(context());
}
vdom
}
}
#[cfg(feature = "server")]
/// Launch a fullstack app with the given root component, contexts, and config.
#[allow(unused)]
pub fn launch(
root: fn() -> Element,
contexts: Vec<Box<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>>,
platform_config: Config,
) -> ! {
let contexts = Arc::new(contexts);
let factory = virtual_dom_factory(root, contexts.clone());
#[cfg(all(feature = "server", not(target_arch = "wasm32")))]
tokio::runtime::Runtime::new()
.unwrap()
.block_on(async move {
launch_server(platform_config, factory, contexts).await;
});
unreachable!("Launching a fullstack app should never return")
}
#[cfg(all(not(feature = "server"), feature = "web"))]
/// Launch a fullstack app with the given root component, contexts, and config.
#[allow(unused)]
pub fn launch(
root: fn() -> Element,
#[allow(unused_mut)] mut contexts: Vec<
Box<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>,
>,
platform_config: Config,
) {
let contexts = Arc::new(contexts);
let mut factory = virtual_dom_factory(root, contexts);
let cfg = platform_config.web_cfg.hydrate(true);
#[cfg(feature = "document")]
let factory = move || {
let mut vdom = factory();
let document = std::rc::Rc::new(crate::document::web::FullstackWebDocument)
as std::rc::Rc<dyn dioxus_lib::prelude::document::Document>;
vdom.provide_root_context(document);
vdom
};
dioxus_web::launch::launch_virtual_dom(factory(), cfg)
}
#[cfg(all(not(any(feature = "server", feature = "web")), feature = "desktop"))]
/// Launch a fullstack app with the given root component, contexts, and config.
#[allow(unused)]
pub fn launch(
root: fn() -> Element,
contexts: Vec<Box<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>>,
platform_config: Config,
) -> ! {
let contexts = Arc::new(contexts);
let factory = virtual_dom_factory(root, contexts);
let cfg = platform_config.desktop_cfg;
dioxus_desktop::launch::launch_virtual_dom(factory(), cfg)
}
#[cfg(all(
not(any(feature = "server", feature = "web", feature = "desktop")),
feature = "mobile"
))]
/// Launch a fullstack app with the given root component, contexts, and config.
#[allow(unused)]
pub fn launch(
root: fn() -> Element,
contexts: Vec<Box<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>>,
platform_config: Config,
) -> ! {
let contexts = Arc::new(contexts);
let factory = virtual_dom_factory(root, contexts.clone());
let cfg = platform_config.mobile_cfg;
dioxus_mobile::launch::launch_virtual_dom(factory(), cfg)
}
#[cfg(not(any(
feature = "server",
feature = "web",
feature = "desktop",
feature = "mobile"
)))]
/// Launch a fullstack app with the given root component, contexts, and config.
#[allow(unused)]
pub fn launch(
root: fn() -> Element,
contexts: Vec<Box<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>>,
platform_config: Config,
) -> ! {
panic!("No platform feature enabled. Please enable one of the following features: axum, desktop, or web to use the launch API.")
}
#[cfg(feature = "server")]
#[allow(unused)]
/// Launch a server application
async fn launch_server(
platform_config: Config,
build_virtual_dom: impl Fn() -> VirtualDom + Send + Sync + 'static,
context_providers: ContextProviders,
) {
// Get the address the server should run on. If the CLI is running, the CLI proxies fullstack into the main address
// and we use the generated address the CLI gives us
let address = dioxus_cli_config::fullstack_address_or_localhost();
#[cfg(feature = "axum")]
{
use crate::axum_adapter::DioxusRouterExt;
#[allow(unused_mut)]
let mut router =
axum::Router::new().register_server_functions_with_context(context_providers);
#[cfg(not(any(feature = "desktop", feature = "mobile")))]
{
use crate::prelude::RenderHandleState;
use crate::prelude::SSRState;
match platform_config.server_cfg.build() {
Ok(cfg) => {
router = router.serve_static_assets();
router = router.fallback(
axum::routing::get(crate::axum_adapter::render_handler).with_state(
RenderHandleState::new_with_virtual_dom_factory(cfg, build_virtual_dom),
),
);
}
Err(err) => {
tracing::trace!("Failed to create render handler. This is expected if you are only using fullstack for desktop/mobile server functions: {}", err);
}
}
}
let router = router.into_make_service();
let listener = tokio::net::TcpListener::bind(address).await.unwrap();
axum::serve(listener, router).await.unwrap();
}
#[cfg(not(feature = "axum"))]
{
panic!("Launching with dioxus fullstack requires the axum feature. If you are using a community fullstack adapter, please check the documentation for that adapter to see how to launch the application.");
}
}
+2 -6
View File
@@ -10,13 +10,9 @@ mod html_storage;
#[cfg(feature = "axum")]
#[cfg_attr(docsrs, doc(cfg(feature = "axum")))]
mod axum_adapter;
pub mod server;
mod config;
mod hooks;
pub mod launch;
pub use config::*;
pub mod document;
#[cfg(feature = "server")]
@@ -39,7 +35,7 @@ pub mod prelude {
#[cfg(feature = "axum")]
#[cfg_attr(docsrs, doc(cfg(feature = "axum")))]
pub use crate::axum_adapter::*;
pub use crate::server::*;
#[cfg(feature = "server")]
#[cfg_attr(docsrs, doc(cfg(feature = "server")))]
+6
View File
@@ -5,6 +5,8 @@ use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use dioxus_lib::prelude::dioxus_core::LaunchConfig;
/// A ServeConfig is used to configure how to serve a Dioxus application. It contains information about how to serve static assets, and what content to render with [`dioxus-ssr`].
#[derive(Clone, Default)]
pub struct ServeConfigBuilder {
@@ -14,6 +16,8 @@ pub struct ServeConfigBuilder {
pub(crate) incremental: Option<dioxus_isrg::IncrementalRendererConfig>,
}
impl LaunchConfig for ServeConfigBuilder {}
impl ServeConfigBuilder {
/// Create a new ServeConfigBuilder with incremental static generation disabled and the default index.html settings
pub fn new() -> Self {
@@ -239,6 +243,8 @@ pub struct ServeConfig {
pub(crate) incremental: Option<dioxus_isrg::IncrementalRendererConfig>,
}
impl LaunchConfig for ServeConfig {}
impl ServeConfig {
/// Create a new ServeConfig
pub fn new() -> Result<Self, UnableToLoadIndex> {
+60
View File
@@ -0,0 +1,60 @@
//! A launch function that creates an axum router for the LaunchBuilder
use std::any::Any;
use dioxus_lib::prelude::*;
/// Launch a fullstack app with the given root component, contexts, and config.
#[allow(unused)]
pub fn launch(
root: fn() -> Element,
contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
platform_config: Vec<Box<dyn Any>>,
) -> ! {
use crate::{ServeConfig, ServeConfigBuilder};
#[cfg(not(target_arch = "wasm32"))]
tokio::runtime::Runtime::new()
.unwrap()
.block_on(async move {
let platform_config = platform_config
.into_iter()
.find_map(|cfg| {
cfg.downcast::<ServeConfig>()
.map(|cfg| Result::Ok(*cfg))
.or_else(|cfg| {
cfg.downcast::<ServeConfigBuilder>()
.map(|builder| builder.build())
})
.ok()
})
.unwrap_or_else(ServeConfig::new);
// Get the address the server should run on. If the CLI is running, the CLI proxies fullstack into the main address
// and we use the generated address the CLI gives us
let address = dioxus_cli_config::fullstack_address_or_localhost();
use crate::server::DioxusRouterExt;
struct TryIntoResult(Result<ServeConfig, crate::UnableToLoadIndex>);
impl TryInto<ServeConfig> for TryIntoResult {
type Error = crate::UnableToLoadIndex;
fn try_into(self) -> Result<ServeConfig, Self::Error> {
self.0
}
}
#[allow(unused_mut)]
let mut router =
axum::Router::new().serve_dioxus_application(TryIntoResult(platform_config), root);
let router = router.into_make_service();
let listener = tokio::net::TcpListener::bind(address).await.unwrap();
axum::serve(listener, router).await.unwrap();
});
unreachable!("Launching a fullstack app should never return")
}
@@ -8,7 +8,7 @@
//! fn main() {
//! #[cfg(feature = "web")]
//! // Hydrate the application on the client
//! launch(app);
//! dioxus::launch(app);
//! #[cfg(feature = "server")]
//! {
//! tokio::runtime::Runtime::new()
@@ -52,6 +52,12 @@
//! }
//! ```
pub mod launch;
#[allow(unused)]
pub(crate) type ContextProviders =
Arc<Vec<Box<dyn Fn() -> Box<dyn std::any::Any> + Send + Sync + 'static>>>;
use axum::routing::*;
use axum::{
body::{self, Body},
@@ -64,7 +70,6 @@ use http::header::*;
use std::sync::Arc;
use crate::launch::ContextProviders;
use crate::prelude::*;
/// A extension trait with utilities for integrating Dioxus with your Axum router.
@@ -177,8 +182,6 @@ where
) -> Self {
use http::method::Method;
let context_providers = Arc::new(context_providers);
for (path, method) in server_fn::axum::server_fn_paths() {
tracing::trace!("Registering server function: {} {}", method, path);
let context_providers = context_providers.clone();
@@ -186,9 +189,10 @@ where
handle_server_fns_inner(
path,
move |server_context| {
for context_provider in context_providers.iter() {
let context = context_provider();
server_context.insert_any(context);
for index in 0..context_providers.len() {
let context_providers = context_providers.clone();
server_context
.insert_boxed_factory(Box::new(move || context_providers[index]()));
}
},
req,
+39 -9
View File
@@ -3,8 +3,7 @@ use std::any::Any;
use std::collections::HashMap;
use std::sync::Arc;
type SendSyncAnyMap =
std::collections::HashMap<std::any::TypeId, Box<dyn Any + Send + Sync + 'static>>;
type SendSyncAnyMap = std::collections::HashMap<std::any::TypeId, ContextType>;
/// A shared context for server functions that contains information about the request and middleware state.
///
@@ -29,6 +28,20 @@ pub struct DioxusServerContext {
pub(crate) parts: Arc<RwLock<http::request::Parts>>,
}
enum ContextType {
Factory(Box<dyn Fn() -> Box<dyn Any> + Send + Sync>),
Value(Box<dyn Any + Send + Sync>),
}
impl ContextType {
fn downcast<T: Clone + 'static>(&self) -> Option<T> {
match self {
ContextType::Value(value) => value.downcast_ref::<T>().cloned(),
ContextType::Factory(factory) => factory().downcast::<T>().ok().map(|v| *v),
}
}
}
#[allow(clippy::derivable_impls)]
impl Default for DioxusServerContext {
fn default() -> Self {
@@ -103,21 +116,38 @@ mod server_fn_impl {
self.shared_context
.read()
.get(&TypeId::of::<T>())
.map(|v| v.downcast_ref::<T>().unwrap().clone())
.map(|v| v.downcast::<T>().unwrap())
}
/// Insert a value into the shared server context
///
///
pub fn insert<T: Any + Send + Sync + 'static>(&self, value: T) {
self.insert_any(Box::new(value));
}
/// Insert a Boxed `Any` value into the shared server context
pub fn insert_any(&self, value: Box<dyn Any + Send + Sync>) {
/// Insert a boxed `Any` value into the shared server context
pub fn insert_any(&self, value: Box<dyn Any + Send + Sync + 'static>) {
self.shared_context
.write()
.insert((*value).type_id(), value);
.insert((*value).type_id(), ContextType::Value(value));
}
/// Insert a factory that creates a non-sync value for the shared server context
pub fn insert_factory<F, T>(&self, value: F)
where
F: Fn() -> T + Send + Sync + 'static,
T: 'static,
{
self.shared_context.write().insert(
TypeId::of::<T>(),
ContextType::Factory(Box::new(move || Box::new(value()))),
);
}
/// Insert a boxed factory that creates a non-sync value for the shared server context
pub fn insert_boxed_factory(&self, value: Box<dyn Fn() -> Box<dyn Any> + Send + Sync>) {
self.shared_context
.write()
.insert((*value()).type_id(), ContextType::Factory(value));
}
/// Get the response parts from the server context
@@ -324,7 +354,7 @@ impl<T: 'static> std::error::Error for NotFoundInServerContext<T> {}
/// ```rust, no_run
/// use dioxus::prelude::*;
///
/// LaunchBuilder::new()
/// dioxus::LaunchBuilder::new()
/// // You can provide context to your whole app (including server functions) with the `with_context` method on the launch builder
/// .with_context(server_only! {
/// 1234567890u32