From 4ffcef16af2807fbb65dfe305088251040e2219f Mon Sep 17 00:00:00 2001 From: Miles Murgaw Date: Wed, 31 Jul 2024 20:14:43 -0400 Subject: [PATCH] Feat: Auto Default Into (#2757) * feat: auto default into * fix: fmt * fix: type infer * feat: text auto into --- packages/core-macro/src/props/mod.rs | 24 ++++++++++++-- packages/core-macro/tests/rsx.rs | 48 ++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/packages/core-macro/src/props/mod.rs b/packages/core-macro/src/props/mod.rs index 809d5d7cb..9e1f91fc8 100644 --- a/packages/core-macro/src/props/mod.rs +++ b/packages/core-macro/src/props/mod.rs @@ -213,6 +213,9 @@ mod field_info { { builder_attr.from_displayable = true; // ToString is both more general and provides a more useful error message than From. If the user tries to use `#[into]`, use ToString instead. + if builder_attr.auto_into { + builder_attr.auto_to_string = true; + } builder_attr.auto_into = false; } @@ -275,6 +278,7 @@ mod field_info { pub docs: Vec, pub skip: bool, pub auto_into: bool, + pub auto_to_string: bool, pub from_displayable: bool, pub strip_option: bool, pub ignore_option: bool, @@ -512,7 +516,7 @@ mod struct_info { use syn::parse::Error; use syn::punctuated::Punctuated; use syn::spanned::Spanned; - use syn::{Expr, Ident}; + use syn::{parse_quote, Expr, Ident}; use crate::props::strip_option; @@ -1368,10 +1372,24 @@ Finally, call `.build()` to create the instance of `{name}`. if !field.builder_attr.extends.is_empty() { quote!(let #name = self.#name;) } else if let Some(ref default) = field.builder_attr.default { + + // If field has `into`, apply it to the default value. + // Ignore any blank defaults as it causes type inference errors. + let is_default = *default == parse_quote!(::core::default::Default::default()); + let mut into = quote!{}; + + if !is_default { + if field.builder_attr.auto_into { + into = quote!{ .into() } + } else if field.builder_attr.auto_to_string { + into = quote!{ .to_string() } + } + } + if field.builder_attr.skip { - quote!(let #name = #default;) + quote!(let #name = #default #into;) } else { - quote!(let #name = #helper_trait_name::into_value(#name, || #default);) + quote!(let #name = #helper_trait_name::into_value(#name, || #default #into);) } } else { quote!(let #name = #name.0;) diff --git a/packages/core-macro/tests/rsx.rs b/packages/core-macro/tests/rsx.rs index f4037f928..d11bb36b2 100644 --- a/packages/core-macro/tests/rsx.rs +++ b/packages/core-macro/tests/rsx.rs @@ -3,3 +3,51 @@ fn rsx() { let t = trybuild::TestCases::new(); t.compile_fail("tests/rsx/trailing-comma-0.rs"); } + +/// This test ensures that automatic `into` conversion occurs for default values. +/// +/// These are compile-time tests. +/// See https://github.com/DioxusLabs/dioxus/issues/2373 +#[cfg(test)] +mod test_default_into { + use dioxus::prelude::*; + + #[derive(Props, Clone, PartialEq)] + struct MyCoolProps { + // Test different into configurations + #[props(into, default = true)] + pub val_into_w_default_val: u16, + + #[props(into, default)] + pub val_into_w_default: u16, + + #[props(default = true.into())] + pub val_default: u16, + + // Test different into configurations with strings + #[props(into, default = "abc")] + pub str_into_w_default_val: String, + + #[props(into, default)] + pub str_into_w_default: String, + + #[props(default = "abc".into())] + pub str_default: String, + + // Test options + #[props(into, default = Some("abc"))] + pub opt_into_w_default_val: Option, + + #[props(into, default)] + pub opt_into_w_default: Option, + + #[props(default = Some("abc"))] + pub opt_default: Option, + + // Test no default + #[props(into)] + pub some_data: bool, + + pub some_other_data: bool, + } +}