1
0
Эх сурвалжийг харах

fix Option<String> in props

Evan Almloff 1 жил өмнө
parent
commit
c3555a7ec0

+ 13 - 5
examples/readme.rs

@@ -1,7 +1,3 @@
-//! Example: README.md showcase
-//!
-//! The example from the README.md.
-
 use dioxus::prelude::*;
 
 fn main() {
@@ -12,8 +8,20 @@ fn app() -> Element {
     let mut count = use_signal(|| 0);
 
     rsx! {
-        h1 { "High-Five counter: {count}" }
+        Child { count: "High-Five counter: {count}" }
+        Child { count: "count" }
         button { onclick: move |_| count += 1, "Up high!" }
         button { onclick: move |_| count -= 1, "Down low!" }
     }
 }
+
+#[derive(Props, Clone, PartialEq)]
+struct ChildProps {
+    count: Option<String>,
+}
+
+fn Child(props: ChildProps) -> Element {
+    rsx! {
+        h1 { "{props.count.unwrap_or_default()}" }
+    }
+}

+ 11 - 6
packages/core-macro/src/props/mod.rs

@@ -3,8 +3,8 @@
 //! However, it has been adopted to fit the Dioxus Props builder pattern.
 //!
 //! For Dioxus, we make a few changes:
-//! - [ ] Automatically implement Into<Option> on the setters (IE the strip setter option)
-//! - [ ] Automatically implement a default of none for optional fields (those explicitly wrapped with Option<T>)
+//! - [x] Automatically implement Into<Option> on the setters (IE the strip setter option)
+//! - [x] Automatically implement a default of none for optional fields (those explicitly wrapped with Option<T>)
 
 use proc_macro2::TokenStream;
 
@@ -956,6 +956,7 @@ Finally, call `.build()` to create the instance of `{name}`.
                 index_after_lifetime_in_generics,
                 syn::GenericArgument::Type(ty_generics_tuple.into()),
             );
+
             let (impl_generics, _, where_clause) = generics.split_for_impl();
             let doc = match field.builder_attr.doc {
                 Some(ref doc) => quote!(#[doc = #doc]),
@@ -963,11 +964,15 @@ Finally, call `.build()` to create the instance of `{name}`.
             };
 
             let arg_type = field_type;
+            // If the field is auto_into, we need to add a generic parameter to the builder for specialization
+            let mut marker = None;
             let (arg_type, arg_expr) =
                 if field.builder_attr.auto_into || field.builder_attr.strip_option {
+                    let marker_ident = syn::Ident::new("__Marker", proc_macro2::Span::call_site());
+                    marker = Some(marker_ident.clone());
                     (
-                        quote!(impl ::core::convert::Into<#arg_type>),
-                        quote!(#field_name.into()),
+                        quote!(impl dioxus_core::prelude::SuperInto<#arg_type, #marker_ident>),
+                        quote!(#field_name.super_into()),
                     )
                 } else if field.builder_attr.from_displayable {
                     (
@@ -998,7 +1003,7 @@ Finally, call `.build()` to create the instance of `{name}`.
                 impl #impl_generics #builder_name < #( #ty_generics ),* > #where_clause {
                     #doc
                     #[allow(clippy::type_complexity)]
-                    pub fn #field_name (self, #field_name: #arg_type) -> #builder_name < #( #target_generics ),* > {
+                    pub fn #field_name < #marker > (self, #field_name: #arg_type) -> #builder_name < #( #target_generics ),* > {
                         let #field_name = (#arg_expr,);
                         let ( #(#descructuring,)* ) = self.fields;
                         #builder_name {
@@ -1018,7 +1023,7 @@ Finally, call `.build()` to create the instance of `{name}`.
                         note = #repeated_fields_error_message
                     )]
                     #[allow(clippy::type_complexity)]
-                    pub fn #field_name (self, _: #repeated_fields_error_type_name) -> #builder_name < #( #target_generics ),* > {
+                    pub fn #field_name< #marker > (self, _: #repeated_fields_error_type_name) -> #builder_name < #( #target_generics ),* > {
                         self
                     }
                 }

+ 4 - 3
packages/core/src/lib.rs

@@ -91,8 +91,9 @@ pub mod prelude {
         remove_future, schedule_update, schedule_update_any, spawn, spawn_forever, suspend,
         try_consume_context, use_drop, use_error_boundary, use_hook, use_hook_with_cleanup,
         AnyValue, Attribute, Component, ComponentFunction, Element, ErrorBoundary, Event,
-        EventHandler, Fragment, HasAttributes, IntoAttributeValue, IntoDynNode, Properties,
-        Runtime, RuntimeGuard, ScopeId, ScopeState, Task, Template, TemplateAttribute,
-        TemplateNode, Throw, VNode, VNodeInner, VirtualDom,
+        EventHandler, Fragment, HasAttributes, IntoAttributeValue, IntoDynNode,
+        OptionStringFromMarker, Properties, Runtime, RuntimeGuard, ScopeId, ScopeState, SuperFrom,
+        SuperInto, Task, Template, TemplateAttribute, TemplateNode, Throw, VNode, VNodeInner,
+        VirtualDom,
     };
 }

+ 72 - 1
packages/core/src/properties.rs

@@ -1,4 +1,4 @@
-use std::any::TypeId;
+use std::{any::TypeId, fmt::Arguments};
 
 use crate::innerlude::*;
 
@@ -118,3 +118,74 @@ impl<F: Fn() -> Element + Clone + 'static> ComponentFunction<(), EmptyMarker> fo
         self()
     }
 }
+
+/// A enhanced version of the `Into` trait that allows with more flexibility.
+pub trait SuperInto<O, M = ()> {
+    /// Convert from a type to another type.
+    fn super_into(self) -> O;
+}
+
+impl<T, O, M> SuperInto<O, M> for T
+where
+    O: SuperFrom<T, M>,
+{
+    fn super_into(self) -> O {
+        O::super_from(self)
+    }
+}
+
+/// A enhanced version of the `From` trait that allows with more flexibility.
+pub trait SuperFrom<T, M = ()> {
+    /// Convert from a type to another type.
+    fn super_from(_: T) -> Self;
+}
+
+// first implement for all types that are that implement the From trait
+impl<T, O> SuperFrom<T, ()> for O
+where
+    O: From<T>,
+{
+    fn super_from(input: T) -> Self {
+        Self::from(input)
+    }
+}
+
+#[doc(hidden)]
+pub struct OptionStringFromMarker;
+
+impl<'a> SuperFrom<&'a str, OptionStringFromMarker> for Option<String> {
+    fn super_from(input: &'a str) -> Self {
+        Some(String::from(input))
+    }
+}
+
+#[doc(hidden)]
+pub struct OptionArgumentsFromMarker;
+
+impl<'a> SuperFrom<Arguments<'a>, OptionArgumentsFromMarker> for Option<String> {
+    fn super_from(input: Arguments<'a>) -> Self {
+        Some(input.to_string())
+    }
+}
+
+#[test]
+#[allow(unused)]
+fn from_props_compiles() {
+    // T -> T works
+    let option: i32 = 0i32.super_into();
+    let option: i32 = 0.super_into(); // Note we don't need type hints on all inputs
+    let option: i128 = 0.super_into();
+    let option: &'static str = "hello world".super_into();
+
+    // // T -> From<T> works
+    let option: i64 = 0i32.super_into();
+    let option: String = "hello world".super_into();
+
+    // T -> Option works
+    let option: Option<i32> = 0i32.super_into();
+    let option: Option<i32> = 0.super_into();
+    let option: Option<i128> = 0.super_into();
+    fn takes_option_string<M>(_: impl SuperInto<Option<String>, M>) {}
+    takes_option_string("hello world");
+    takes_option_string("hello world".to_string());
+}

+ 3 - 0
packages/signals/src/lib.rs

@@ -39,3 +39,6 @@ pub use read::*;
 
 mod write;
 pub use write::*;
+
+mod props;
+pub use props::*;

+ 28 - 0
packages/signals/src/props.rs

@@ -0,0 +1,28 @@
+use crate::Signal;
+use dioxus_core::prelude::*;
+
+#[doc(hidden)]
+pub struct SignalFromMarker<M>(std::marker::PhantomData<M>);
+
+impl<T, O, M> SuperFrom<T, SignalFromMarker<M>> for Signal<O>
+where
+    O: SuperFrom<T, M>,
+{
+    fn super_from(input: T) -> Self {
+        Signal::new(O::super_from(input))
+    }
+}
+
+#[test]
+#[allow(unused)]
+fn into_signal_compiles() {
+    fn takes_signal_string<M>(_: impl SuperInto<Signal<String>, M>) {}
+
+    fn takes_option_signal_string<M>(_: impl SuperInto<Signal<Option<String>>, M>) {}
+
+    fn don_t_run() {
+        takes_signal_string("hello world");
+        takes_signal_string(Signal::new(String::from("hello world")));
+        takes_option_signal_string("hello world");
+    }
+}