diff --git a/packages/desktop/src/launch.rs b/packages/desktop/src/launch.rs index c3962f43d..406776507 100644 --- a/packages/desktop/src/launch.rs +++ b/packages/desktop/src/launch.rs @@ -11,7 +11,7 @@ use tao::event::{Event, StartCause, WindowEvent}; /// /// This will block the main thread, and *must* be spawned on the main thread. This function does not assume any runtime /// and is equivalent to calling launch_with_props with the tokio feature disabled. -pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, desktop_config: Config) { +pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, desktop_config: Config) -> ! { let (event_loop, mut app) = App::new(desktop_config, virtual_dom); event_loop.run(move |window_event, _, control_flow| { @@ -66,7 +66,7 @@ pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, desktop_config: Conf } /// Launches the WebView and runs the event loop, with configuration and root props. -pub fn launch_virtual_dom(virtual_dom: VirtualDom, desktop_config: Config) { +pub fn launch_virtual_dom(virtual_dom: VirtualDom, desktop_config: Config) -> ! { #[cfg(feature = "tokio")] tokio::runtime::Builder::new_multi_thread() .enable_all() @@ -77,7 +77,9 @@ pub fn launch_virtual_dom(virtual_dom: VirtualDom, desktop_config: Config) { })); #[cfg(not(feature = "tokio"))] - launch_virtual_dom_blocking(virtual_dom, desktop_config) + launch_virtual_dom_blocking(virtual_dom, desktop_config); + + unreachable!("The desktop launch function will never exit") } /// Launches the WebView and runs the event loop, with configuration and root props. @@ -85,7 +87,7 @@ pub fn launch( root: fn() -> Element, contexts: Vec Box>>, platform_config: Config, -) { +) -> ! { let mut virtual_dom = VirtualDom::new(root); for context in contexts { diff --git a/packages/dioxus/src/launch.rs b/packages/dioxus/src/launch.rs index 489202607..de54818f6 100644 --- a/packages/dioxus/src/launch.rs +++ b/packages/dioxus/src/launch.rs @@ -55,7 +55,7 @@ impl LaunchBuilder { )] pub fn new() -> LaunchBuilder { LaunchBuilder { - launch_fn: current_platform::launch, + launch_fn: |root, contexts, cfg| current_platform::launch(root, contexts, cfg), contexts: Vec::new(), platform_config: None, } @@ -77,7 +77,7 @@ impl LaunchBuilder { #[cfg_attr(docsrs, doc(cfg(feature = "desktop")))] pub fn desktop() -> LaunchBuilder { LaunchBuilder { - launch_fn: dioxus_desktop::launch::launch, + launch_fn: |root, contexts, cfg| dioxus_desktop::launch::launch(root, contexts, cfg), contexts: Vec::new(), platform_config: None, } @@ -88,7 +88,7 @@ impl LaunchBuilder { #[cfg_attr(docsrs, doc(cfg(feature = "fullstack")))] pub fn fullstack() -> LaunchBuilder { LaunchBuilder { - launch_fn: dioxus_fullstack::launch::launch, + launch_fn: |root, contexts, cfg| dioxus_fullstack::launch::launch(root, contexts, cfg), contexts: Vec::new(), platform_config: None, } @@ -99,7 +99,7 @@ impl LaunchBuilder { #[cfg_attr(docsrs, doc(cfg(feature = "mobile")))] pub fn mobile() -> LaunchBuilder { LaunchBuilder { - launch_fn: dioxus_mobile::launch::launch, + launch_fn: |root, contexts, cfg| dioxus_mobile::launch::launch(root, contexts, cfg), contexts: Vec::new(), platform_config: None, } @@ -188,12 +188,23 @@ impl LaunchBuilder { self } + // Static generation is the only platform that may exit. We can't use the `!` type here + #[cfg(any(feature = "static-generation", feature = "web"))] /// Launch your application. pub fn launch(self, app: fn() -> Element) { let cfg = self.platform_config.unwrap_or_default(); (self.launch_fn)(app, self.contexts, cfg) } + + #[cfg(not(any(feature = "static-generation", feature = "web")))] + /// Launch your application. + pub fn launch(self, app: fn() -> Element) -> ! { + let cfg = self.platform_config.unwrap_or_default(); + + (self.launch_fn)(app, self.contexts, cfg); + unreachable!("Launching an application will never exit") + } } /// Re-export the platform we expect the user wants @@ -308,36 +319,47 @@ mod current_platform { root: fn() -> dioxus_core::Element, contexts: Vec>, platform_config: (), - ) { + ) -> ! { #[cfg(feature = "third-party-renderer")] panic!("No first party renderer feature enabled. It looks like you are trying to use a third party renderer. You will need to use the launch function from the third party renderer crate."); - panic!("No platform feature enabled. Please enable one of the following features: liveview, desktop, mobile, web, tui, fullstack to use the launch API."); + panic!("No platform feature enabled. Please enable one of the following features: liveview, desktop, mobile, web, tui, fullstack to use the launch API.") } } -/// Launch your application without any additional configuration. See [`LaunchBuilder`] for more options. -// If you aren't using a third party renderer and this is not a docs.rs build, generate a warning about no renderer being enabled -#[cfg_attr( - all(not(any( - docsrs, - feature = "third-party-renderer", - feature = "liveview", - feature = "desktop", - feature = "mobile", - feature = "web", - feature = "fullstack", - feature = "static-generation" - ))), - deprecated( - note = "No renderer is enabled. You must enable a renderer feature on the dioxus crate before calling the launch function.\nAdd `web`, `desktop`, `mobile`, `fullstack`, or `static-generation` to the `features` of dioxus field in your Cargo.toml.\n# Example\n```toml\n# ...\n[dependencies]\ndioxus = { version = \"0.5.0\", features = [\"web\"] }\n# ...\n```" - ) -)] -pub fn launch(app: fn() -> Element) { - #[allow(deprecated)] - LaunchBuilder::new().launch(app) +// ! is unstable, so we can't name the type with an alias. Instead we need to generate different variants of items with macros +macro_rules! impl_launch { + ($($return_type:tt),*) => { + /// Launch your application without any additional configuration. See [`LaunchBuilder`] for more options. + // If you aren't using a third party renderer and this is not a docs.rs build, generate a warning about no renderer being enabled + #[cfg_attr( + all(not(any( + docsrs, + feature = "third-party-renderer", + feature = "liveview", + feature = "desktop", + feature = "mobile", + feature = "web", + feature = "fullstack", + feature = "static-generation" + ))), + deprecated( + note = "No renderer is enabled. You must enable a renderer feature on the dioxus crate before calling the launch function.\nAdd `web`, `desktop`, `mobile`, `fullstack`, or `static-generation` to the `features` of dioxus field in your Cargo.toml.\n# Example\n```toml\n# ...\n[dependencies]\ndioxus = { version = \"0.5.0\", features = [\"web\"] }\n# ...\n```" + ) + )] + pub fn launch(app: fn() -> Element) -> $($return_type)* { + #[allow(deprecated)] + LaunchBuilder::new().launch(app) + } + }; } +// Static generation is the only platform that may exit. We can't use the `!` type here +#[cfg(any(feature = "static-generation", feature = "web"))] +impl_launch!(()); +#[cfg(not(any(feature = "static-generation", feature = "web")))] +impl_launch!(!); + #[cfg(feature = "web")] #[cfg_attr(docsrs, doc(cfg(feature = "web")))] /// Launch your web application without any additional configuration. See [`LaunchBuilder`] for more options. diff --git a/packages/fullstack/src/launch.rs b/packages/fullstack/src/launch.rs index 7d4497551..3882bd3a4 100644 --- a/packages/fullstack/src/launch.rs +++ b/packages/fullstack/src/launch.rs @@ -6,6 +6,39 @@ use dioxus_lib::prelude::{Element, VirtualDom}; pub use crate::Config; +fn virtual_dom_factory( + root: fn() -> Element, + contexts: Vec Box + Send + Sync>>, +) -> 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 + Send + Sync>>, + platform_config: Config, +) -> ! { + let factory = virtual_dom_factory(root, contexts); + #[cfg(all(feature = "server", not(target_arch = "wasm32")))] + tokio::runtime::Runtime::new() + .unwrap() + .block_on(async move { + platform_config.launch_server(factory).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( @@ -13,40 +46,36 @@ pub fn launch( contexts: Vec Box + Send + Sync>>, platform_config: Config, ) { - let virtual_dom_factory = move || { - let mut vdom = VirtualDom::new(root); - for context in &contexts { - vdom.insert_any_root_context(context()); - } - vdom - }; + let factory = virtual_dom_factory(root, contexts); + let cfg = platform_config.web_cfg.hydrate(true); + dioxus_web::launch::launch_virtual_dom(factory(), cfg) +} - #[cfg(all(feature = "server", not(target_arch = "wasm32")))] - tokio::runtime::Runtime::new() - .unwrap() - .block_on(async move { - platform_config.launch_server(virtual_dom_factory).await; - }); +#[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 + Send + Sync>>, + platform_config: Config, +) -> ! { + let factory = virtual_dom_factory(root, contexts); + let cfg = platform_config.desktop_cfg; + dioxus_desktop::launch::launch_virtual_dom(factory(), cfg) +} - #[cfg(not(feature = "server"))] - { - #[cfg(feature = "web")] - { - // TODO: this should pull the props from the document - let cfg = platform_config.web_cfg.hydrate(true); - dioxus_web::launch::launch_virtual_dom(virtual_dom_factory(), cfg); - } - - #[cfg(feature = "desktop")] - { - let cfg = platform_config.desktop_cfg; - dioxus_desktop::launch::launch_virtual_dom(virtual_dom_factory(), cfg) - } - - #[cfg(feature = "mobile")] - { - let cfg = platform_config.mobile_cfg; - dioxus_mobile::launch::launch_virtual_dom(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 + Send + Sync>>, + platform_config: Config, +) { + let factory = virtual_dom_factory(root, contexts); + let cfg = platform_config.mobile_cfg; + dioxus_mobile::launch::launch_virtual_dom(factory(), cfg) } diff --git a/packages/liveview/src/launch.rs b/packages/liveview/src/launch.rs index 2adbb5212..6c1d25ce1 100644 --- a/packages/liveview/src/launch.rs +++ b/packages/liveview/src/launch.rs @@ -8,7 +8,7 @@ pub fn launch( root: fn() -> Element, contexts: Vec Box + Send + Sync>>, platform_config: Config, -) { +) -> ! { #[cfg(feature = "multi-threaded")] let mut builder = tokio::runtime::Builder::new_multi_thread(); #[cfg(not(feature = "multi-threaded"))] @@ -28,4 +28,6 @@ pub fn launch( .launch() .await; }); + + panic!("Launching a liveview app should never return") } diff --git a/packages/web/src/launch.rs b/packages/web/src/launch.rs index 0a86a78cc..0d0383d49 100644 --- a/packages/web/src/launch.rs +++ b/packages/web/src/launch.rs @@ -16,7 +16,7 @@ pub fn launch( for context in contexts { vdom.insert_any_root_context(context()); } - launch_virtual_dom(vdom, platform_config); + launch_virtual_dom(vdom, platform_config) } /// Launch the web application with a prebuild virtual dom @@ -30,5 +30,5 @@ pub fn launch_virtual_dom(vdom: VirtualDom, platform_config: Config) { /// Launch the web application with the given root component and config pub fn launch_cfg(root: fn() -> Element, platform_config: Config) { - launch_virtual_dom(VirtualDom::new(root), platform_config); + launch_virtual_dom(VirtualDom::new(root), platform_config) }