Skip to main content

dioxus/
launch.rs

1#![allow(clippy::new_without_default)]
2#![allow(unused)]
3use dioxus_config_macro::*;
4use dioxus_core::{Element, LaunchConfig};
5use std::any::Any;
6
7use crate::prelude::*;
8
9/// Launch your Dioxus application with the given root component, context and config.
10/// The platform will be determined from cargo features.
11///
12/// For a builder API, see `LaunchBuilder` defined in the `dioxus` crate.
13///
14/// # Feature selection
15///
16/// - `web`: Enables the web platform.
17/// - `desktop`: Enables the desktop platform.
18/// - `mobile`: Enables the mobile (ios + android webview) platform.
19/// - `server`: Enables the server (axum + server-side-rendering) platform.
20/// - `liveview`: Enables the liveview (websocke) platform.
21/// - `native`: Enables the native (wgpu + winit renderer) platform.
22///
23/// Currently `native` is its own platform that is not compatible with desktop or mobile since it
24/// unifies both platforms into one. If "desktop" and "native" are enabled, then the native renderer
25/// will be used.
26///
27/// # Feature priority
28///
29/// If multiple renderers are enabled, the order of priority goes:
30///
31/// 1. liveview
32/// 2. server
33/// 3. native
34/// 4. desktop
35/// 5. mobile
36/// 6. web
37///
38/// However, we don't recommend enabling multiple renderers at the same time due to feature conflicts
39/// and bloating of the binary size.
40///
41/// # Example
42/// ```rust, no_run
43/// use dioxus::prelude::*;
44///
45/// fn main() {
46///     dioxus::launch(app);
47/// }
48///
49/// fn app() -> Element {
50///     rsx! {
51///         div { "Hello, world!" }
52///     }
53/// }
54/// ```
55pub fn launch(app: fn() -> Element) {
56    #[allow(deprecated)]
57    LaunchBuilder::new().launch(app)
58}
59
60/// A builder for a fullstack app.
61#[must_use]
62pub struct LaunchBuilder {
63    platform: KnownPlatform,
64    contexts: Vec<ContextFn>,
65    configs: Vec<Box<dyn Any>>,
66}
67
68pub type LaunchFn = fn(fn() -> Element, Vec<ContextFn>, Vec<Box<dyn Any>>);
69
70/// A context function is a Send and Sync closure that returns a boxed trait object
71pub type ContextFn = Box<dyn Fn() -> Box<dyn Any> + Send + Sync + 'static>;
72
73enum KnownPlatform {
74    Web,
75    Desktop,
76    Mobile,
77    Server,
78    Liveview,
79    Native,
80    Other(LaunchFn),
81}
82
83#[allow(clippy::redundant_closure)] // clippy doesn't understand our coercion to fn
84impl LaunchBuilder {
85    /// Create a new builder for your application. This will create a launch configuration for the current platform based on the features enabled on the `dioxus` crate.
86    // 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
87    #[cfg_attr(
88        all(not(any(
89            docsrs,
90            feature = "third-party-renderer",
91            feature = "liveview",
92            feature = "desktop",
93            feature = "mobile",
94            feature = "web",
95            feature = "fullstack",
96        ))),
97        deprecated(
98            note = "No renderer is enabled. You must enable a renderer feature on the dioxus crate before calling the launch function.\nAdd `web`, `desktop`, `mobile`, or `fullstack` 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```"
99        )
100    )]
101    pub fn new() -> LaunchBuilder {
102        let platform = if cfg!(feature = "native") {
103            KnownPlatform::Native
104        } else if cfg!(feature = "desktop") {
105            KnownPlatform::Desktop
106        } else if cfg!(feature = "mobile") {
107            KnownPlatform::Mobile
108        } else if cfg!(feature = "web") {
109            KnownPlatform::Web
110        } else if cfg!(feature = "server") {
111            KnownPlatform::Server
112        } else if cfg!(feature = "liveview") {
113            KnownPlatform::Liveview
114        } else {
115            panic!("No platform feature enabled. Please enable one of the following features: liveview, desktop, mobile, web, tui, fullstack to use the launch API.")
116        };
117
118        LaunchBuilder {
119            platform,
120            contexts: Vec::new(),
121            configs: Vec::new(),
122        }
123    }
124
125    /// Launch your web application.
126    #[cfg(feature = "web")]
127    #[cfg_attr(docsrs, doc(cfg(feature = "web")))]
128    pub fn web() -> LaunchBuilder {
129        LaunchBuilder {
130            platform: KnownPlatform::Web,
131            contexts: Vec::new(),
132            configs: Vec::new(),
133        }
134    }
135
136    /// Launch your desktop application.
137    #[cfg(feature = "desktop")]
138    #[cfg_attr(docsrs, doc(cfg(feature = "desktop")))]
139    pub fn desktop() -> LaunchBuilder {
140        LaunchBuilder {
141            platform: KnownPlatform::Desktop,
142            contexts: Vec::new(),
143            configs: Vec::new(),
144        }
145    }
146
147    /// Launch your fullstack axum server.
148    #[cfg(all(feature = "fullstack", feature = "server"))]
149    #[cfg_attr(docsrs, doc(cfg(all(feature = "fullstack", feature = "server"))))]
150    pub fn server() -> LaunchBuilder {
151        LaunchBuilder {
152            platform: KnownPlatform::Server,
153            contexts: Vec::new(),
154            configs: Vec::new(),
155        }
156    }
157
158    /// Launch your fullstack application.
159    #[cfg(feature = "mobile")]
160    #[cfg_attr(docsrs, doc(cfg(feature = "mobile")))]
161    pub fn mobile() -> LaunchBuilder {
162        LaunchBuilder {
163            platform: KnownPlatform::Mobile,
164            contexts: Vec::new(),
165            configs: Vec::new(),
166        }
167    }
168
169    /// Provide a custom launch function for your application.
170    ///
171    /// Useful for third party renderers to tap into the launch builder API without having to reimplement it.
172    ///
173    /// # Example
174    /// ```rust, no_run
175    /// use dioxus::prelude::*;
176    /// use std::any::Any;
177    ///
178    /// #[derive(Default)]
179    /// struct Config;
180    ///
181    /// fn my_custom_launcher(root: fn() -> Element, contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>, cfg: Vec<Box<dyn Any>>) {
182    ///     println!("launching with root: {:?}", root());
183    ///     loop {
184    ///         println!("running...");
185    ///     }
186    /// }
187    ///
188    /// fn app() -> Element {
189    ///     rsx! {
190    ///         div { "Hello, world!" }
191    ///     }
192    /// }
193    ///
194    /// dioxus::LaunchBuilder::custom(my_custom_launcher).launch(app);
195    /// ```
196    pub fn custom(launch_fn: LaunchFn) -> LaunchBuilder {
197        LaunchBuilder {
198            platform: KnownPlatform::Other(launch_fn),
199            contexts: vec![],
200            configs: Vec::new(),
201        }
202    }
203
204    /// Inject state into the root component's context that is created on the thread that the app is launched on.
205    ///
206    /// # Example
207    /// ```rust, no_run
208    /// use dioxus::prelude::*;
209    /// use std::any::Any;
210    ///
211    /// #[derive(Default)]
212    /// struct MyState {
213    ///     value: i32,
214    /// }
215    ///
216    /// fn app() -> Element {
217    ///     rsx! {
218    ///         div { "Hello, world!" }
219    ///     }
220    /// }
221    ///
222    /// dioxus::LaunchBuilder::new()
223    ///     .with_context_provider(|| Box::new(MyState { value: 42 }))
224    ///     .launch(app);
225    /// ```
226    pub fn with_context_provider(
227        mut self,
228        state: impl Fn() -> Box<dyn Any> + Send + Sync + 'static,
229    ) -> Self {
230        self.contexts.push(Box::new(state));
231        self
232    }
233
234    /// Inject state into the root component's context.
235    ///
236    /// # Example
237    /// ```rust, no_run
238    /// use dioxus::prelude::*;
239    /// use std::any::Any;
240    ///
241    /// #[derive(Clone)]
242    /// struct MyState {
243    ///     value: i32,
244    /// }
245    ///
246    /// fn app() -> Element {
247    ///     rsx! {
248    ///         div { "Hello, world!" }
249    ///     }
250    /// }
251    ///
252    /// dioxus::LaunchBuilder::new()
253    ///     .with_context(MyState { value: 42 })
254    ///     .launch(app);
255    /// ```
256    pub fn with_context(mut self, state: impl Any + Clone + Send + Sync + 'static) -> Self {
257        self.contexts
258            .push(Box::new(move || Box::new(state.clone())));
259        self
260    }
261
262    /// Provide a platform-specific config to the builder.
263    ///
264    /// # Example
265    /// ```rust, no_run
266    /// use dioxus::prelude::*;
267    /// use dioxus_desktop::{Config, WindowBuilder};
268    ///
269    /// fn app() -> Element {
270    ///     rsx! {
271    ///         div { "Hello, world!" }
272    ///     }
273    /// }
274    ///
275    /// dioxus::LaunchBuilder::new()
276    ///    .with_cfg(desktop! {
277    ///        Config::new().with_window(
278    ///            WindowBuilder::new()
279    ///                .with_title("My App")
280    ///        )
281    ///     })
282    ///     .launch(app);
283    /// ```
284    pub fn with_cfg(mut self, config: impl LaunchConfig) -> Self {
285        self.configs.push(Box::new(config));
286        self
287    }
288
289    /// Launch your application.
290    #[allow(clippy::diverging_sub_expression)]
291    pub fn launch(self, app: fn() -> Element) {
292        let Self {
293            platform,
294            contexts,
295            configs,
296        } = self;
297
298        // Make sure to turn on the logger if the user specified the logger feaature
299        #[cfg(feature = "logger")]
300        dioxus_logger::initialize_default();
301
302        // Set any flags if we're running under fullstack
303        #[cfg(feature = "fullstack")]
304        {
305            use dioxus_fullstack::{get_server_url, set_server_url};
306
307            // Make sure to set the server_fn endpoint if the user specified the fullstack feature
308            // We only set this on native targets
309            #[cfg(any(feature = "desktop", feature = "mobile", feature = "native"))]
310            if get_server_url().is_empty() {
311                let serverurl = format!(
312                    "http://{}:{}",
313                    std::env::var("DIOXUS_DEVSERVER_IP")
314                        .unwrap_or_else(|_| "127.0.0.1".to_string()),
315                    std::env::var("DIOXUS_DEVSERVER_PORT").unwrap_or_else(|_| "8080".to_string())
316                )
317                .leak();
318
319                set_server_url(serverurl);
320            }
321
322            // If there is a base path set, call server functions from that base path
323            #[cfg(feature = "web")]
324            if let Some(base_path) = dioxus_cli_config::base_path() {
325                let base_path = base_path.trim_matches('/');
326                set_server_url(format!("{}/{}", get_server_url(), base_path).leak());
327            }
328        }
329
330        // If native is specified, we override the webview launcher
331        #[cfg(feature = "native")]
332        if matches!(platform, KnownPlatform::Native) {
333            return dioxus_native::launch_cfg(app, contexts, configs);
334        }
335
336        #[cfg(feature = "mobile")]
337        if matches!(platform, KnownPlatform::Mobile) {
338            return dioxus_desktop::launch::launch(app, contexts, configs);
339        }
340
341        #[cfg(feature = "desktop")]
342        if matches!(platform, KnownPlatform::Desktop) {
343            return dioxus_desktop::launch::launch(app, contexts, configs);
344        }
345
346        #[cfg(feature = "server")]
347        if matches!(platform, KnownPlatform::Server) {
348            return dioxus_server::launch_cfg(app, contexts, configs);
349        }
350
351        #[cfg(feature = "web")]
352        if matches!(platform, KnownPlatform::Web) {
353            return dioxus_web::launch::launch(app, contexts, configs);
354        }
355
356        #[cfg(feature = "liveview")]
357        if matches!(platform, KnownPlatform::Liveview) {
358            return dioxus_liveview::launch::launch(app, contexts, configs);
359        }
360
361        // If the platform is not one of the above, then we assume it's a custom platform
362        if let KnownPlatform::Other(launch_fn) = platform {
363            return launch_fn(app, contexts, configs);
364        }
365
366        // If we're here, then we have no platform feature enabled and third-party-renderer is enabled
367        if cfg!(feature = "third-party-renderer") {
368            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.");
369        }
370
371        panic!("No platform feature enabled. Please enable one of the following features: liveview, desktop, mobile, web, tui, fullstack to use the launch API.")
372    }
373}