|
@@ -1,173 +1,218 @@
|
|
//! Launch helper macros for fullstack apps
|
|
//! Launch helper macros for fullstack apps
|
|
#![allow(unused)]
|
|
#![allow(unused)]
|
|
-
|
|
|
|
-#[macro_export]
|
|
|
|
-/// Launch a server with a router
|
|
|
|
-macro_rules! launch_router {
|
|
|
|
- (@router_config) => {
|
|
|
|
- dioxus_fullstack::router::FullstackRouterConfig::default()
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- (@router_config $router_cfg:expr) => {
|
|
|
|
- $router_cfg
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- (@$address:expr, $route:ty, $(cfg: $router_cfg:expr,)? {$($rule:ident $(: $cfg:expr)?,)*}) => {
|
|
|
|
- dioxus_fullstack::launch!(
|
|
|
|
- @$address,
|
|
|
|
- dioxus_fullstack::router::RouteWithCfg::<$route>,
|
|
|
|
- (dioxus_fullstack::launch_router!(@router_config $($router_cfg)?)),
|
|
|
|
- {
|
|
|
|
- $($rule $(: $cfg)?,)*
|
|
|
|
- }
|
|
|
|
- )
|
|
|
|
- };
|
|
|
|
|
|
+use crate::prelude::*;
|
|
|
|
+use dioxus::prelude::*;
|
|
|
|
+#[cfg(feature = "router")]
|
|
|
|
+use dioxus_router::prelude::*;
|
|
|
|
+
|
|
|
|
+/// A builder for a fullstack app.
|
|
|
|
+pub struct LaunchBuilder<Props: Clone> {
|
|
|
|
+ component: Component<Props>,
|
|
|
|
+ #[cfg(not(feature = "ssr"))]
|
|
|
|
+ props: Props,
|
|
|
|
+ #[cfg(feature = "ssr")]
|
|
|
|
+ server_fn_route: &'static str,
|
|
|
|
+ #[cfg(feature = "ssr")]
|
|
|
|
+ server_cfg: ServeConfigBuilder<Props>,
|
|
|
|
+ #[cfg(feature = "ssr")]
|
|
|
|
+ addr: std::net::SocketAddr,
|
|
|
|
+ #[cfg(feature = "web")]
|
|
|
|
+ web_cfg: dioxus_web::Config,
|
|
|
|
+ #[cfg(feature = "desktop")]
|
|
|
|
+ desktop_cfg: dioxus_desktop::Config,
|
|
}
|
|
}
|
|
|
|
|
|
-#[macro_export]
|
|
|
|
-/// Launch a server
|
|
|
|
-macro_rules! launch {
|
|
|
|
- (@web_cfg $server_cfg:ident $wcfg:expr) => {
|
|
|
|
- #[cfg(feature = "web")]
|
|
|
|
- let web_cfg = $wcfg;
|
|
|
|
- };
|
|
|
|
|
|
+impl<Props: Clone + serde::Serialize + serde::de::DeserializeOwned + Send + Sync + 'static>
|
|
|
|
+ LaunchBuilder<Props>
|
|
|
|
+{
|
|
|
|
+ /// Create a new builder for a fullstack app.
|
|
|
|
+ pub fn new(component: Component<Props>) -> Self
|
|
|
|
+ where
|
|
|
|
+ Props: Default,
|
|
|
|
+ {
|
|
|
|
+ Self::new_with_props(component, Default::default())
|
|
|
|
+ }
|
|
|
|
|
|
- (@web_cfg $server_cfg:ident) => {
|
|
|
|
- #[cfg(feature = "web")]
|
|
|
|
- let web_cfg = dioxus_web::Config::new();
|
|
|
|
- };
|
|
|
|
|
|
+ /// Create a new builder for a fullstack app with props.
|
|
|
|
+ pub fn new_with_props(component: Component<Props>, props: Props) -> Self
|
|
|
|
+ where
|
|
|
|
+ Props: Default,
|
|
|
|
+ {
|
|
|
|
+ Self {
|
|
|
|
+ component,
|
|
|
|
+ #[cfg(not(feature = "ssr"))]
|
|
|
|
+ props,
|
|
|
|
+ #[cfg(feature = "ssr")]
|
|
|
|
+ server_fn_route: "",
|
|
|
|
+ #[cfg(feature = "ssr")]
|
|
|
|
+ addr: std::net::SocketAddr::from(([127, 0, 0, 1], 8080)),
|
|
|
|
+ #[cfg(feature = "ssr")]
|
|
|
|
+ server_cfg: ServeConfigBuilder::new(component, props),
|
|
|
|
+ #[cfg(feature = "web")]
|
|
|
|
+ web_cfg: dioxus_web::Config::default(),
|
|
|
|
+ #[cfg(feature = "desktop")]
|
|
|
|
+ desktop_cfg: dioxus_desktop::Config::default(),
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
- (@serve_cfg $server_cfg:ident $cfg:expr) => {
|
|
|
|
- #[cfg(feature = "ssr")]
|
|
|
|
- let $server_cfg = $cfg;
|
|
|
|
- };
|
|
|
|
|
|
+ /// Set the address to serve the app on.
|
|
|
|
+ #[cfg(feature = "ssr")]
|
|
|
|
+ pub fn addr(self, addr: impl Into<std::net::SocketAddr>) -> Self {
|
|
|
|
+ let addr = addr.into();
|
|
|
|
+ Self { addr, ..self }
|
|
|
|
+ }
|
|
|
|
|
|
- (@hot_reload $server_cfg:ident) => {
|
|
|
|
- #[cfg(feature = "ssr")]
|
|
|
|
- {
|
|
|
|
- dioxus_fullstack::prelude::hot_reload_init!(dioxus_hot_reload::Config::new().with_rebuild_callback(|| {
|
|
|
|
- std::process::Command::new("cargo")
|
|
|
|
- .args(&["run", "--features", "ssr"])
|
|
|
|
- .spawn()
|
|
|
|
- .unwrap()
|
|
|
|
- .wait()
|
|
|
|
- .unwrap();
|
|
|
|
- std::process::Command::new("cargo")
|
|
|
|
- .args(&["run", "--features", "web"])
|
|
|
|
- .spawn()
|
|
|
|
- .unwrap()
|
|
|
|
- .wait()
|
|
|
|
- .unwrap();
|
|
|
|
- true
|
|
|
|
- }));
|
|
|
|
|
|
+ /// Set the route to the server functions.
|
|
|
|
+ #[cfg(feature = "ssr")]
|
|
|
|
+ pub fn server_fn_route(self, server_fn_route: &'static str) -> Self {
|
|
|
|
+ Self {
|
|
|
|
+ server_fn_route,
|
|
|
|
+ ..self
|
|
}
|
|
}
|
|
- };
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- (@hot_reload $server_cfg:ident $hot_reload_cfg:expr) => {
|
|
|
|
- #[cfg(feature = "ssr")]
|
|
|
|
- {
|
|
|
|
- dioxus_fullstack::prelude::hot_reload_init!($hot_reload_cfg);
|
|
|
|
|
|
+ /// Set the incremental renderer config.
|
|
|
|
+ #[cfg(feature = "ssr")]
|
|
|
|
+ pub fn incremental(self, cfg: IncrementalRendererConfig) -> Self {
|
|
|
|
+ Self {
|
|
|
|
+ server_cfg: self.server_cfg.incremental(cfg),
|
|
|
|
+ ..self
|
|
}
|
|
}
|
|
- };
|
|
|
|
-
|
|
|
|
- (@incremental $server_cfg:ident) => {
|
|
|
|
- #[cfg(feature = "ssr")]
|
|
|
|
- let $server_cfg = $server_cfg.incremental(dioxus_fullstack::prelude::IncrementalRendererConfig::default());
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- (@incremental $server_cfg:ident $cfg:expr) => {
|
|
|
|
- #[cfg(feature = "ssr")]
|
|
|
|
- let $server_cfg = $server_cfg.incremental($cfg);
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- (@props_type) => {
|
|
|
|
- Default::default()
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- (@props_type $props:expr) => {
|
|
|
|
- $props
|
|
|
|
- };
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- (@ $address:expr, $comp:path, $(( $props:expr ),)? {$($rule:ident $(: $cfg:expr)?,)*}) => {
|
|
|
|
- #[cfg(feature = "web")]
|
|
|
|
- {
|
|
|
|
- #[allow(unused)]
|
|
|
|
- let web_cfg = dioxus_web::Config::new();
|
|
|
|
|
|
+ /// Set the server config.
|
|
|
|
+ #[cfg(feature = "ssr")]
|
|
|
|
+ pub fn server_cfg(self, server_cfg: ServeConfigBuilder<Props>) -> Self {
|
|
|
|
+ Self { server_cfg, ..self }
|
|
|
|
+ }
|
|
|
|
|
|
- $(
|
|
|
|
- dioxus_fullstack::prelude::launch!(@$rule server_cfg $($cfg)?);
|
|
|
|
- )*
|
|
|
|
|
|
+ /// Set the web config.
|
|
|
|
+ #[cfg(feature = "web")]
|
|
|
|
+ pub fn web_cfg(self, web_cfg: dioxus_web::Config) -> Self {
|
|
|
|
+ Self { web_cfg, ..self }
|
|
|
|
+ }
|
|
|
|
|
|
- dioxus_web::launch_with_props(
|
|
|
|
- $comp,
|
|
|
|
- dioxus_fullstack::prelude::get_root_props_from_document().expect("Failed to get root props from document"),
|
|
|
|
- web_cfg.hydrate(true),
|
|
|
|
- );
|
|
|
|
|
|
+ /// Set the desktop config.
|
|
|
|
+ #[cfg(feature = "desktop")]
|
|
|
|
+ pub fn desktop_cfg(self, desktop_cfg: dioxus_desktop::Config) -> Self {
|
|
|
|
+ Self {
|
|
|
|
+ desktop_cfg,
|
|
|
|
+ ..self
|
|
}
|
|
}
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Launch the app.
|
|
|
|
+ pub fn launch(self) {
|
|
#[cfg(feature = "ssr")]
|
|
#[cfg(feature = "ssr")]
|
|
|
|
+ tokio::runtime::Runtime::new()
|
|
|
|
+ .unwrap()
|
|
|
|
+ .block_on(async move {
|
|
|
|
+ self.launch_server().await;
|
|
|
|
+ });
|
|
|
|
+ #[cfg(not(feature = "ssr"))]
|
|
{
|
|
{
|
|
- let server_cfg = dioxus_fullstack::prelude::ServeConfigBuilder::new($comp, dioxus_fullstack::prelude::launch!(@props_type $($props)?));
|
|
|
|
|
|
+ #[cfg(feature = "web")]
|
|
|
|
+ self.launch_web();
|
|
|
|
+ #[cfg(feature = "desktop")]
|
|
|
|
+ self.launch_desktop();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
- $(
|
|
|
|
- dioxus_fullstack::prelude::launch!(@$rule server_cfg $($cfg)?);
|
|
|
|
- )*
|
|
|
|
|
|
+ #[cfg(feature = "web")]
|
|
|
|
+ fn launch_web(self) {
|
|
|
|
+ let cfg = self.web_cfg.hydrate(true);
|
|
|
|
+ dioxus_web::launch_with_props(self.component, get_root_props_from_document().unwrap(), cfg);
|
|
|
|
+ }
|
|
|
|
|
|
- tokio::runtime::Runtime::new()
|
|
|
|
- .unwrap()
|
|
|
|
- .block_on(async move {
|
|
|
|
- let addr = std::net::SocketAddr::from($address);
|
|
|
|
|
|
+ #[cfg(feature = "desktop")]
|
|
|
|
+ fn launch_desktop(self) {
|
|
|
|
+ let cfg = self.desktop_cfg;
|
|
|
|
+ dioxus_desktop::launch_with_props(self.component, self.props, cfg);
|
|
|
|
+ }
|
|
|
|
|
|
- dioxus_fullstack::launch::launch_server(addr, server_cfg.build()).await;
|
|
|
|
- });
|
|
|
|
|
|
+ /// Launch a server with the given configuration
|
|
|
|
+ /// This will use the routing integration of the currently enabled integration feature
|
|
|
|
+ #[cfg(feature = "ssr")]
|
|
|
|
+ async fn launch_server(self) {
|
|
|
|
+ let addr = self.addr;
|
|
|
|
+ println!("Listening on {}", addr);
|
|
|
|
+ let cfg = self.server_cfg.build();
|
|
|
|
+ let server_fn_route = self.server_fn_route;
|
|
|
|
+ #[cfg(all(feature = "axum", not(feature = "warp"), not(feature = "salvo")))]
|
|
|
|
+ {
|
|
|
|
+ use crate::adapters::axum_adapter::{render_handler, DioxusRouterExt};
|
|
|
|
+ use axum::routing::get;
|
|
|
|
+ use tower::ServiceBuilder;
|
|
|
|
+
|
|
|
|
+ let ssr_state = SSRState::new(&cfg);
|
|
|
|
+ let router = axum::Router::new().register_server_fns(server_fn_route);
|
|
|
|
+ #[cfg(not(feature = "desktop"))]
|
|
|
|
+ let router = router
|
|
|
|
+ .serve_static_assets(cfg.assets_path)
|
|
|
|
+ .connect_hot_reload()
|
|
|
|
+ .fallback(get(render_handler).with_state((cfg, ssr_state)));
|
|
|
|
+ let router = router
|
|
|
|
+ .layer(
|
|
|
|
+ ServiceBuilder::new()
|
|
|
|
+ .layer(tower_http::compression::CompressionLayer::new().gzip(true)),
|
|
|
|
+ )
|
|
|
|
+ .into_make_service();
|
|
|
|
+ axum::Server::bind(&addr).serve(router).await.unwrap();
|
|
|
|
+ }
|
|
|
|
+ #[cfg(all(feature = "warp", not(feature = "axum"), not(feature = "salvo")))]
|
|
|
|
+ {
|
|
|
|
+ use warp::Filter;
|
|
|
|
+ // First register the server functions
|
|
|
|
+ let router = register_server_fns(server_fn_route);
|
|
|
|
+ #[cfg(not(feature = "desktop"))]
|
|
|
|
+ let router = {
|
|
|
|
+ // Serve the dist folder and the index.html file
|
|
|
|
+ let serve_dir = warp::fs::dir(cfg.assets_path);
|
|
|
|
+
|
|
|
|
+ router
|
|
|
|
+ .or(connect_hot_reload())
|
|
|
|
+ // Then the index route
|
|
|
|
+ .or(warp::path::end().and(render_ssr(cfg.clone())))
|
|
|
|
+ // Then the static assets
|
|
|
|
+ .or(serve_dir)
|
|
|
|
+ // Then all other routes
|
|
|
|
+ .or(render_ssr(cfg))
|
|
|
|
+ };
|
|
|
|
+ warp::serve(router.boxed().with(warp::filters::compression::gzip()))
|
|
|
|
+ .run(addr)
|
|
|
|
+ .await;
|
|
|
|
+ }
|
|
|
|
+ #[cfg(all(feature = "salvo", not(feature = "axum"), not(feature = "warp")))]
|
|
|
|
+ {
|
|
|
|
+ use crate::adapters::salvo_adapter::{DioxusRouterExt, SSRHandler};
|
|
|
|
+ use salvo::conn::Listener;
|
|
|
|
+ let router = salvo::Router::new().register_server_fns(server_fn_route);
|
|
|
|
+ #[cfg(not(feature = "desktop"))]
|
|
|
|
+ let router = router
|
|
|
|
+ .serve_static_assets(cfg.assets_path)
|
|
|
|
+ .connect_hot_reload()
|
|
|
|
+ .push(salvo::Router::with_path("/<**any_path>").get(SSRHandler::new(cfg)));
|
|
|
|
+ let router = router.hoop(
|
|
|
|
+ salvo::compression::Compression::new()
|
|
|
|
+ .enable_gzip(salvo::prelude::CompressionLevel::Default),
|
|
|
|
+ );
|
|
|
|
+ salvo::Server::new(salvo::conn::tcp::TcpListener::new(addr).bind().await)
|
|
|
|
+ .serve(router)
|
|
|
|
+ .await;
|
|
}
|
|
}
|
|
- };
|
|
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
-/// Launch a server with the given configeration
|
|
|
|
-/// This will use the routing intigration of the currently enabled intigration feature
|
|
|
|
-#[cfg(feature = "ssr")]
|
|
|
|
-pub async fn launch_server<P: Clone + serde::Serialize + Send + Sync + 'static>(
|
|
|
|
- addr: std::net::SocketAddr,
|
|
|
|
- cfg: crate::prelude::ServeConfig<P>,
|
|
|
|
-) {
|
|
|
|
- #[cfg(all(feature = "axum", not(feature = "warp"), not(feature = "salvo")))]
|
|
|
|
- {
|
|
|
|
- use crate::adapters::axum_adapter::DioxusRouterExt;
|
|
|
|
- use tower::ServiceBuilder;
|
|
|
|
-
|
|
|
|
- axum::Server::bind(&addr)
|
|
|
|
- .serve(
|
|
|
|
- axum::Router::new()
|
|
|
|
- .serve_dioxus_application("", cfg)
|
|
|
|
- .layer(
|
|
|
|
- ServiceBuilder::new()
|
|
|
|
- .layer(tower_http::compression::CompressionLayer::new().gzip(true)),
|
|
|
|
- )
|
|
|
|
- .into_make_service(),
|
|
|
|
- )
|
|
|
|
- .await
|
|
|
|
- .unwrap();
|
|
|
|
- }
|
|
|
|
- #[cfg(all(feature = "warp", not(feature = "axum"), not(feature = "salvo")))]
|
|
|
|
- {
|
|
|
|
- use warp::Filter;
|
|
|
|
- warp::serve(
|
|
|
|
- crate::prelude::serve_dioxus_application("", cfg)
|
|
|
|
- .with(warp::filters::compression::gzip()),
|
|
|
|
- )
|
|
|
|
- .run(addr)
|
|
|
|
- .await;
|
|
|
|
- }
|
|
|
|
- #[cfg(all(feature = "salvo", not(feature = "axum"), not(feature = "warp")))]
|
|
|
|
- {
|
|
|
|
- use crate::adapters::salvo_adapter::DioxusRouterExt;
|
|
|
|
- use salvo::conn::Listener;
|
|
|
|
- let router = salvo::Router::new().serve_dioxus_application("", cfg).hoop(
|
|
|
|
- salvo::compression::Compression::new()
|
|
|
|
- .enable_gzip(salvo::prelude::CompressionLevel::Default),
|
|
|
|
- );
|
|
|
|
- salvo::Server::new(salvo::conn::tcp::TcpListener::new(addr).bind().await)
|
|
|
|
- .serve(router)
|
|
|
|
- .await;
|
|
|
|
|
|
+#[cfg(feature = "router")]
|
|
|
|
+impl<R: Routable> LaunchBuilder<crate::router::FullstackRouterConfig<R>>
|
|
|
|
+where
|
|
|
|
+ <R as std::str::FromStr>::Err: std::fmt::Display,
|
|
|
|
+ R: Clone + serde::Serialize + serde::de::DeserializeOwned + Send + Sync + 'static,
|
|
|
|
+{
|
|
|
|
+ /// Create a new launch builder for the given router.
|
|
|
|
+ pub fn router() -> Self {
|
|
|
|
+ let component = crate::router::RouteWithCfg::<R>;
|
|
|
|
+ let props = crate::router::FullstackRouterConfig::default();
|
|
|
|
+ Self::new_with_props(component, props)
|
|
}
|
|
}
|
|
}
|
|
}
|