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}