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:
1 parent
2d7bef44e8
commit
20d146d9bd
126 files changed
+618
-989
No files matched your search
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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(
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
@@ -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")))]
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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,
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user