1
0

launch.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. //! Launch helper macros for fullstack apps
  2. #![allow(clippy::new_without_default)]
  3. #![allow(unused)]
  4. use dioxus_config_macro::*;
  5. use dioxus_core::LaunchConfig;
  6. use std::any::Any;
  7. use crate::prelude::*;
  8. /// A builder for a fullstack app.
  9. #[must_use]
  10. pub struct LaunchBuilder {
  11. launch_fn: LaunchFn,
  12. contexts: Vec<ContextFn>,
  13. configs: Vec<Box<dyn Any>>,
  14. }
  15. pub type LaunchFn = fn(fn() -> Element, Vec<ContextFn>, Vec<Box<dyn Any>>);
  16. /// A context function is a Send and Sync closure that returns a boxed trait object
  17. pub type ContextFn = Box<dyn Fn() -> Box<dyn Any> + Send + Sync + 'static>;
  18. #[cfg(any(
  19. feature = "fullstack",
  20. feature = "static-generation",
  21. feature = "liveview"
  22. ))]
  23. type ValidContext = SendContext;
  24. #[cfg(not(any(
  25. feature = "fullstack",
  26. feature = "static-generation",
  27. feature = "liveview"
  28. )))]
  29. type ValidContext = UnsendContext;
  30. type SendContext = dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync + 'static;
  31. type UnsendContext = dyn Fn() -> Box<dyn Any> + 'static;
  32. #[allow(clippy::redundant_closure)] // clippy doesnt doesn't understand our coercion to fn
  33. impl LaunchBuilder {
  34. /// 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.
  35. // 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
  36. #[cfg_attr(
  37. all(not(any(
  38. docsrs,
  39. feature = "third-party-renderer",
  40. feature = "liveview",
  41. feature = "desktop",
  42. feature = "mobile",
  43. feature = "web",
  44. feature = "fullstack",
  45. feature = "static-generation"
  46. ))),
  47. deprecated(
  48. 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```"
  49. )
  50. )]
  51. pub fn new() -> LaunchBuilder {
  52. LaunchBuilder {
  53. // We can't use the `current_platform::launch` function directly because it may return ! or ()
  54. launch_fn: |root, contexts, cfg| current_platform::launch(root, contexts, cfg),
  55. contexts: Vec::new(),
  56. configs: Vec::new(),
  57. }
  58. }
  59. /// Launch your web application.
  60. #[cfg(feature = "web")]
  61. #[cfg_attr(docsrs, doc(cfg(feature = "web")))]
  62. pub fn web() -> LaunchBuilder {
  63. LaunchBuilder {
  64. launch_fn: web_launch,
  65. contexts: Vec::new(),
  66. configs: Vec::new(),
  67. }
  68. }
  69. /// Launch your desktop application.
  70. #[cfg(feature = "desktop")]
  71. #[cfg_attr(docsrs, doc(cfg(feature = "desktop")))]
  72. pub fn desktop() -> LaunchBuilder {
  73. LaunchBuilder {
  74. launch_fn: |root, contexts, cfg| dioxus_desktop::launch::launch(root, contexts, cfg),
  75. contexts: Vec::new(),
  76. configs: Vec::new(),
  77. }
  78. }
  79. /// Launch your fullstack axum server.
  80. #[cfg(all(feature = "fullstack", feature = "server"))]
  81. #[cfg_attr(docsrs, doc(cfg(all(feature = "fullstack", feature = "server"))))]
  82. pub fn server() -> LaunchBuilder {
  83. LaunchBuilder {
  84. launch_fn: |root, contexts, cfg| {
  85. dioxus_fullstack::server::launch::launch(root, contexts, cfg)
  86. },
  87. contexts: Vec::new(),
  88. configs: Vec::new(),
  89. }
  90. }
  91. /// Launch your static site generation application.
  92. #[cfg(all(feature = "static-generation", feature = "server"))]
  93. #[cfg_attr(
  94. docsrs,
  95. doc(cfg(all(feature = "static-generation", feature = "server")))
  96. )]
  97. pub fn static_generation() -> LaunchBuilder {
  98. LaunchBuilder {
  99. launch_fn: |root, contexts, cfg| {
  100. dioxus_static_site_generation::launch::launch(root, contexts, cfg)
  101. },
  102. contexts: Vec::new(),
  103. configs: Vec::new(),
  104. }
  105. }
  106. /// Launch your fullstack application.
  107. #[cfg(feature = "mobile")]
  108. #[cfg_attr(docsrs, doc(cfg(feature = "mobile")))]
  109. pub fn mobile() -> LaunchBuilder {
  110. LaunchBuilder {
  111. launch_fn: |root, contexts, cfg| dioxus_mobile::launch::launch(root, contexts, cfg),
  112. contexts: Vec::new(),
  113. configs: Vec::new(),
  114. }
  115. }
  116. /// Provide a custom launch function for your application.
  117. ///
  118. /// Useful for third party renderers to tap into the launch builder API without having to reimplement it.
  119. ///
  120. /// # Example
  121. /// ```rust, no_run
  122. /// use dioxus::prelude::*;
  123. /// use std::any::Any;
  124. ///
  125. /// #[derive(Default)]
  126. /// struct Config;
  127. ///
  128. /// fn my_custom_launcher(root: fn() -> Element, contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>, cfg: Vec<Box<dyn Any>>) {
  129. /// println!("launching with root: {:?}", root());
  130. /// loop {
  131. /// println!("running...");
  132. /// }
  133. /// }
  134. ///
  135. /// fn app() -> Element {
  136. /// rsx! {
  137. /// div { "Hello, world!" }
  138. /// }
  139. /// }
  140. ///
  141. /// dioxus::LaunchBuilder::custom(my_custom_launcher).launch(app);
  142. /// ```
  143. pub fn custom(launch_fn: LaunchFn) -> LaunchBuilder {
  144. LaunchBuilder {
  145. launch_fn,
  146. contexts: vec![],
  147. configs: Vec::new(),
  148. }
  149. }
  150. }
  151. impl LaunchBuilder {
  152. /// Inject state into the root component's context that is created on the thread that the app is launched on.
  153. pub fn with_context_provider(
  154. mut self,
  155. state: impl Fn() -> Box<dyn Any> + Send + Sync + 'static,
  156. ) -> Self {
  157. self.contexts.push(Box::new(state));
  158. self
  159. }
  160. /// Inject state into the root component's context.
  161. pub fn with_context(mut self, state: impl Any + Clone + Send + Sync + 'static) -> Self {
  162. self.contexts
  163. .push(Box::new(move || Box::new(state.clone())));
  164. self
  165. }
  166. }
  167. impl LaunchBuilder {
  168. /// Provide a platform-specific config to the builder.
  169. pub fn with_cfg(mut self, config: impl LaunchConfig) -> Self {
  170. self.configs.push(Box::new(config));
  171. self
  172. }
  173. // Static generation is the only platform that may exit. We can't use the `!` type here
  174. #[cfg(any(feature = "static-generation", feature = "web"))]
  175. /// Launch your application.
  176. pub fn launch(self, app: fn() -> Element) {
  177. let cfg = self.configs;
  178. (self.launch_fn)(app, self.contexts, cfg)
  179. }
  180. #[cfg(not(any(feature = "static-generation", feature = "web")))]
  181. /// Launch your application.
  182. pub fn launch(self, app: fn() -> Element) -> ! {
  183. let cfg = self.configs;
  184. (self.launch_fn)(app, self.contexts, cfg);
  185. unreachable!("Launching an application will never exit")
  186. }
  187. }
  188. /// Re-export the platform we expect the user wants
  189. ///
  190. /// If multiple platforms are enabled, we use this priority (from highest to lowest):
  191. /// - `fullstack`
  192. /// - `desktop`
  193. /// - `mobile`
  194. /// - `static-generation`
  195. /// - `web`
  196. /// - `liveview`
  197. mod current_platform {
  198. #[cfg(all(feature = "fullstack", feature = "server"))]
  199. pub use dioxus_fullstack::server::launch::*;
  200. #[cfg(all(
  201. feature = "desktop",
  202. not(all(feature = "fullstack", feature = "server"))
  203. ))]
  204. pub use dioxus_desktop::launch::*;
  205. #[cfg(all(
  206. feature = "mobile",
  207. not(feature = "desktop"),
  208. not(all(feature = "fullstack", feature = "server"))
  209. ))]
  210. pub use dioxus_mobile::launch::*;
  211. #[cfg(all(
  212. all(feature = "static-generation", feature = "server"),
  213. not(all(feature = "fullstack", feature = "server")),
  214. not(feature = "desktop"),
  215. not(feature = "mobile")
  216. ))]
  217. pub use dioxus_static_site_generation::launch::*;
  218. #[cfg(all(
  219. feature = "web",
  220. not(all(feature = "fullstack", feature = "server")),
  221. not(all(feature = "static-generation", feature = "server")),
  222. not(feature = "desktop"),
  223. not(feature = "mobile"),
  224. ))]
  225. pub fn launch(
  226. root: fn() -> dioxus_core::Element,
  227. contexts: Vec<super::ContextFn>,
  228. platform_config: Vec<Box<dyn std::any::Any>>,
  229. ) {
  230. super::web_launch(root, contexts, platform_config);
  231. }
  232. #[cfg(all(
  233. feature = "liveview",
  234. not(all(feature = "fullstack", feature = "server")),
  235. not(all(feature = "static-generation", feature = "server")),
  236. not(feature = "desktop"),
  237. not(feature = "mobile"),
  238. not(feature = "web"),
  239. ))]
  240. pub use dioxus_liveview::launch::*;
  241. #[cfg(not(any(
  242. feature = "liveview",
  243. all(feature = "fullstack", feature = "server"),
  244. all(feature = "static-generation", feature = "server"),
  245. feature = "desktop",
  246. feature = "mobile",
  247. feature = "web",
  248. )))]
  249. pub fn launch(
  250. root: fn() -> dioxus_core::Element,
  251. contexts: Vec<super::ContextFn>,
  252. platform_config: Vec<Box<dyn std::any::Any>>,
  253. ) -> ! {
  254. #[cfg(feature = "third-party-renderer")]
  255. 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.");
  256. panic!("No platform feature enabled. Please enable one of the following features: liveview, desktop, mobile, web, tui, fullstack to use the launch API.")
  257. }
  258. }
  259. // ! is unstable, so we can't name the type with an alias. Instead we need to generate different variants of items with macros
  260. macro_rules! impl_launch {
  261. ($($return_type:tt),*) => {
  262. /// Launch your application without any additional configuration. See [`LaunchBuilder`] for more options.
  263. pub fn launch(app: fn() -> Element) -> $($return_type)* {
  264. #[allow(deprecated)]
  265. LaunchBuilder::new().launch(app)
  266. }
  267. };
  268. }
  269. // Static generation is the only platform that may exit. We can't use the `!` type here
  270. #[cfg(any(feature = "static-generation", feature = "web"))]
  271. impl_launch!(());
  272. #[cfg(not(any(feature = "static-generation", feature = "web")))]
  273. impl_launch!(!);
  274. #[cfg(feature = "web")]
  275. fn web_launch(
  276. root: fn() -> dioxus_core::Element,
  277. contexts: Vec<super::ContextFn>,
  278. platform_config: Vec<Box<dyn std::any::Any>>,
  279. ) {
  280. // If the server feature is enabled, launch the client with hydration enabled
  281. #[cfg(any(feature = "static-generation", feature = "fullstack"))]
  282. {
  283. let platform_config = platform_config
  284. .into_iter()
  285. .find_map(|cfg| cfg.downcast::<dioxus_web::Config>().ok())
  286. .unwrap_or_default()
  287. .hydrate(true);
  288. let factory = move || {
  289. let mut vdom = dioxus_core::VirtualDom::new(root);
  290. for context in contexts {
  291. vdom.insert_any_root_context(context());
  292. }
  293. #[cfg(feature = "document")]
  294. {
  295. #[cfg(feature = "fullstack")]
  296. use dioxus_fullstack::document;
  297. #[cfg(all(feature = "static-generation", not(feature = "fullstack")))]
  298. use dioxus_static_site_generation::document;
  299. let document = std::rc::Rc::new(document::web::FullstackWebDocument)
  300. as std::rc::Rc<dyn crate::prelude::Document>;
  301. vdom.provide_root_context(document);
  302. }
  303. vdom
  304. };
  305. dioxus_web::launch::launch_virtual_dom(factory(), platform_config)
  306. }
  307. #[cfg(not(any(feature = "static-generation", feature = "fullstack")))]
  308. dioxus_web::launch::launch(root, contexts, platform_config);
  309. }