Check type of launch config (#2125)

* Check type of launch config

* fix cargo check

* compile when using an explicit platform with other platforms enabled

* fix formatting

* fix overlapping TryIntoConfig implementations

* fix desktop headless tests

---------

Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
This commit is contained in:
Evan Almloff 2024-03-27 14:08:05 -05:00 committed by GitHub
parent 44f3047780
commit 86d1dba699
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 125 additions and 43 deletions

View file

@ -13,7 +13,7 @@ use dioxus::html::input_data::keyboard_types::Key;
use dioxus::prelude::*;
fn main() {
LaunchBuilder::new()
LaunchBuilder::desktop()
.with_cfg(desktop!({
use dioxus::desktop::{Config, LogicalSize, WindowBuilder};
Config::new().with_window(

View file

@ -4,7 +4,7 @@
use dioxus::prelude::*;
fn main() {
LaunchBuilder::desktop()
LaunchBuilder::new()
.with_cfg(
dioxus::desktop::Config::new().with_custom_index(
r#"

View file

@ -15,14 +15,14 @@ use dioxus::desktop::{Config, LogicalSize, WindowBuilder};
use dioxus::prelude::*;
fn main() {
LaunchBuilder::desktop()
.with_cfg(
LaunchBuilder::new()
.with_cfg(desktop! {
Config::new().with_window(
WindowBuilder::new()
.with_title("Doggo Fetcher")
.with_inner_size(LogicalSize::new(600.0, 800.0)),
),
)
)
})
.launch(app)
}

View file

@ -15,7 +15,7 @@ pub fn server_only(input: TokenStream) -> TokenStream {
}
} else {
quote! {
|| {}
{}
}
}
.into()
@ -30,7 +30,7 @@ pub fn client(input: TokenStream) -> TokenStream {
}
} else {
quote! {
|| {}
{}
}
}
.into()
@ -45,7 +45,7 @@ pub fn web(input: TokenStream) -> TokenStream {
}
} else {
quote! {
|| {}
{}
}
}
.into()
@ -60,7 +60,22 @@ pub fn desktop(input: TokenStream) -> TokenStream {
}
} else {
quote! {
|| {}
{}
}
}
.into()
}
#[proc_macro]
pub fn mobile(input: TokenStream) -> TokenStream {
if cfg!(feature = "mobile") {
let input = TokenStream2::from(input);
quote! {
#input
}
} else {
quote! {
{}
}
}
.into()
@ -75,7 +90,7 @@ pub fn fullstack(input: TokenStream) -> TokenStream {
}
} else {
quote! {
|| {}
{}
}
}
.into()
@ -90,7 +105,7 @@ pub fn ssr(input: TokenStream) -> TokenStream {
}
} else {
quote! {
|| {}
{}
}
}
.into()
@ -105,7 +120,7 @@ pub fn liveview(input: TokenStream) -> TokenStream {
}
} else {
quote! {
|| {}
{}
}
}
.into()

View file

@ -1,6 +1,7 @@
//! Launch helper macros for fullstack apps
#![allow(clippy::new_without_default)]
#![allow(unused)]
use dioxus_config_macro::*;
use std::any::Any;
use crate::prelude::*;
@ -11,7 +12,7 @@ pub struct LaunchBuilder<Cfg: 'static = (), ContextFn: ?Sized = ValidContext> {
launch_fn: LaunchFn<Cfg, ContextFn>,
contexts: Vec<Box<ContextFn>>,
platform_config: Option<Box<dyn Any>>,
platform_config: Option<Cfg>,
}
pub type LaunchFn<Cfg, Context> = fn(fn() -> Element, Vec<Box<Context>>, Cfg);
@ -126,52 +127,118 @@ impl<Cfg> LaunchBuilder<Cfg, SendContext> {
}
}
/// A trait for converting a type into a platform-specific config:
/// - A unit value will be converted into `None`
/// - Any config will be converted into `Some(config)`
/// - If the config is for another platform, it will be converted into `None`
pub trait TryIntoConfig<Config = Self> {
fn into_config(self) -> Option<Config>;
}
// A config can always be converted into itself
impl<Cfg> TryIntoConfig<Cfg> for Cfg {
fn into_config(self) -> Option<Cfg> {
Some(self)
}
}
// The unit type can be converted into the current platform config.
// This makes it possible to use the `desktop!`, `web!`, etc macros with the launch API.
#[cfg(any(
feature = "liveview",
feature = "desktop",
feature = "mobile",
feature = "web",
feature = "fullstack"
))]
impl TryIntoConfig<current_platform::Config> for () {
fn into_config(self) -> Option<current_platform::Config> {
None
}
}
impl<Cfg: Default + 'static, ContextFn: ?Sized> LaunchBuilder<Cfg, ContextFn> {
/// Provide a platform-specific config to the builder.
pub fn with_cfg<CG: 'static>(mut self, config: impl Into<Option<CG>>) -> Self {
if let Some(config) = config.into() {
self.platform_config = Some(Box::new(config));
}
pub fn with_cfg(mut self, config: impl TryIntoConfig<Cfg>) -> Self {
self.platform_config = self.platform_config.or(config.into_config());
self
}
/// Launch your application.
pub fn launch(self, app: fn() -> Element) {
let cfg: Box<Cfg> = self
.platform_config
.and_then(|c| c.downcast().ok())
.unwrap_or_default();
let cfg = self.platform_config.unwrap_or_default();
(self.launch_fn)(app, self.contexts, *cfg)
(self.launch_fn)(app, self.contexts, cfg)
}
}
/// Re-export the platform we expect the user wants
///
/// If multiple platforms are enabled, we use this priority (from highest to lowest):
/// - `fullstack`
/// - `desktop`
/// - `mobile`
/// - `web`
/// - `liveview`
mod current_platform {
#[cfg(all(feature = "desktop", not(feature = "fullstack")))]
pub use dioxus_desktop::launch::*;
macro_rules! if_else_cfg {
(if $attr:meta { $then:item } else { $else:item }) => {
#[cfg($attr)]
$then
#[cfg(not($attr))]
$else
};
}
use crate::prelude::TryIntoConfig;
#[cfg(all(feature = "mobile", not(feature = "fullstack")))]
pub use dioxus_mobile::launch::*;
#[cfg(any(feature = "desktop", feature = "mobile"))]
if_else_cfg! {
if not(feature = "fullstack") {
pub use dioxus_desktop::launch::*;
} else {
impl TryIntoConfig<crate::launch::current_platform::Config> for ::dioxus_desktop::Config {
fn into_config(self) -> Option<crate::launch::current_platform::Config> {
None
}
}
}
}
#[cfg(feature = "fullstack")]
pub use dioxus_fullstack::launch::*;
#[cfg(all(
feature = "web",
not(any(feature = "desktop", feature = "mobile", feature = "fullstack"))
))]
pub use dioxus_web::launch::*;
#[cfg(feature = "web")]
if_else_cfg! {
if not(any(feature = "desktop", feature = "mobile", feature = "fullstack")) {
pub use dioxus_web::launch::*;
} else {
impl TryIntoConfig<crate::launch::current_platform::Config> for ::dioxus_web::Config {
fn into_config(self) -> Option<crate::launch::current_platform::Config> {
None
}
}
}
}
#[cfg(all(
feature = "liveview",
not(any(
feature = "web",
feature = "desktop",
feature = "mobile",
feature = "fullstack"
))
))]
pub use dioxus_liveview::launch::*;
#[cfg(feature = "liveview")]
if_else_cfg! {
if
not(any(
feature = "web",
feature = "desktop",
feature = "mobile",
feature = "fullstack"
))
{
pub use dioxus_liveview::launch::*;
} else {
impl<R: ::dioxus_liveview::LiveviewRouter> TryIntoConfig<crate::launch::current_platform::Config> for ::dioxus_liveview::Config<R> {
fn into_config(self) -> Option<crate::launch::current_platform::Config> {
None
}
}
}
}
#[cfg(not(any(
feature = "liveview",
@ -210,7 +277,7 @@ pub fn launch(app: fn() -> Element) {
#[cfg_attr(docsrs, doc(cfg(feature = "web")))]
/// Launch your web application without any additional configuration. See [`LaunchBuilder`] for more options.
pub fn launch_web(app: fn() -> Element) {
LaunchBuilder::web().launch(app)
LaunchBuilder::new().launch(app)
}
#[cfg(feature = "desktop")]