mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-22 20:23:09 +00:00
fix Option<String> in props
This commit is contained in:
parent
30ef29d195
commit
c3555a7ec0
6 changed files with 131 additions and 15 deletions
|
@ -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()}" }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -39,3 +39,6 @@ pub use read::*;
|
|||
|
||||
mod write;
|
||||
pub use write::*;
|
||||
|
||||
mod props;
|
||||
pub use props::*;
|
||||
|
|
28
packages/signals/src/props.rs
Normal file
28
packages/signals/src/props.rs
Normal file
|
@ -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");
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue