Skip to main content
This is unreleased documentation for Yew Next version.
For up-to-date documentation, see the latest version on docs.rs.

yew/html/conversion/
into_prop_value.rs

1use std::borrow::Cow;
2use std::rc::Rc;
3use std::sync::Arc;
4
5use implicit_clone::unsync::{IArray, IMap};
6pub use implicit_clone::ImplicitClone;
7
8use crate::callback::Callback;
9use crate::html::{BaseComponent, ChildrenRenderer, Component, Scope};
10use crate::virtual_dom::{AttrValue, VChild, VList, VNode, VText};
11
12impl<Comp: Component> ImplicitClone for Scope<Comp> {}
13// TODO there are still a few missing
14
15/// A trait similar to `Into<T>` which allows conversion to a value of a `Properties` struct.
16pub trait IntoPropValue<T> {
17    /// Convert `self` to a value of a `Properties` struct.
18    fn into_prop_value(self) -> T;
19}
20
21impl<T> IntoPropValue<T> for T {
22    #[inline]
23    fn into_prop_value(self) -> T {
24        self
25    }
26}
27
28impl<T> IntoPropValue<T> for &T
29where
30    T: ImplicitClone,
31{
32    #[inline]
33    fn into_prop_value(self) -> T {
34        self.clone()
35    }
36}
37
38impl<T> IntoPropValue<Option<T>> for T {
39    #[inline]
40    fn into_prop_value(self) -> Option<T> {
41        Some(self)
42    }
43}
44
45impl<T> IntoPropValue<Option<T>> for &T
46where
47    T: ImplicitClone,
48{
49    #[inline]
50    fn into_prop_value(self) -> Option<T> {
51        Some(self.clone())
52    }
53}
54
55impl<I, O, F> IntoPropValue<Callback<I, O>> for F
56where
57    F: 'static + Fn(I) -> O,
58{
59    #[inline]
60    fn into_prop_value(self) -> Callback<I, O> {
61        Callback::from(self)
62    }
63}
64
65impl<I, O, F> IntoPropValue<Option<Callback<I, O>>> for F
66where
67    F: 'static + Fn(I) -> O,
68{
69    #[inline]
70    fn into_prop_value(self) -> Option<Callback<I, O>> {
71        Some(Callback::from(self))
72    }
73}
74
75impl<I, O, F> IntoPropValue<Option<Callback<I, O>>> for Option<F>
76where
77    F: 'static + Fn(I) -> O,
78{
79    #[inline]
80    fn into_prop_value(self) -> Option<Callback<I, O>> {
81        self.map(Callback::from)
82    }
83}
84
85impl<T, C> IntoPropValue<ChildrenRenderer<C>> for VChild<T>
86where
87    T: BaseComponent,
88    C: Clone + Into<VNode>,
89    VChild<T>: Into<C>,
90{
91    #[inline]
92    fn into_prop_value(self) -> ChildrenRenderer<C> {
93        ChildrenRenderer::new(vec![self.into()])
94    }
95}
96
97impl<T, C> IntoPropValue<Option<ChildrenRenderer<C>>> for VChild<T>
98where
99    T: BaseComponent,
100    C: Clone + Into<VNode>,
101    VChild<T>: Into<C>,
102{
103    #[inline]
104    fn into_prop_value(self) -> Option<ChildrenRenderer<C>> {
105        Some(ChildrenRenderer::new(vec![self.into()]))
106    }
107}
108
109impl<T, C> IntoPropValue<Option<ChildrenRenderer<C>>> for Option<VChild<T>>
110where
111    T: BaseComponent,
112    C: Clone + Into<VNode>,
113    VChild<T>: Into<C>,
114{
115    #[inline]
116    fn into_prop_value(self) -> Option<ChildrenRenderer<C>> {
117        self.map(|m| ChildrenRenderer::new(vec![m.into()]))
118    }
119}
120
121impl<T, R> IntoPropValue<ChildrenRenderer<R>> for Vec<T>
122where
123    T: Into<R>,
124    R: Clone + Into<VNode>,
125{
126    #[inline]
127    fn into_prop_value(self) -> ChildrenRenderer<R> {
128        ChildrenRenderer::new(self.into_iter().map(|m| m.into()).collect::<Vec<_>>())
129    }
130}
131
132impl<T> IntoPropValue<VNode> for VChild<T>
133where
134    T: BaseComponent,
135{
136    #[inline]
137    fn into_prop_value(self) -> VNode {
138        VNode::from(self)
139    }
140}
141
142impl IntoPropValue<VNode> for VList {
143    #[inline]
144    fn into_prop_value(self) -> VNode {
145        VNode::VList(Rc::new(self))
146    }
147}
148impl IntoPropValue<VNode> for VText {
149    #[inline]
150    fn into_prop_value(self) -> VNode {
151        VNode::VText(self)
152    }
153}
154
155impl IntoPropValue<VNode> for () {
156    #[inline]
157    fn into_prop_value(self) -> VNode {
158        VNode::default()
159    }
160}
161
162impl IntoPropValue<VNode> for ChildrenRenderer<VNode> {
163    #[inline]
164    fn into_prop_value(self) -> VNode {
165        VNode::VList(Rc::new(self.into()))
166    }
167}
168
169impl IntoPropValue<VNode> for &ChildrenRenderer<VNode> {
170    #[inline]
171    fn into_prop_value(self) -> VNode {
172        VNode::VList(Rc::new(VList::from(self.children.clone())))
173    }
174}
175
176impl IntoPropValue<ChildrenRenderer<VNode>> for VNode {
177    #[inline]
178    fn into_prop_value(self) -> ChildrenRenderer<VNode> {
179        ChildrenRenderer::new(vec![self])
180    }
181}
182
183impl IntoPropValue<ChildrenRenderer<VNode>> for VText {
184    #[inline]
185    fn into_prop_value(self) -> ChildrenRenderer<VNode> {
186        ChildrenRenderer::new(vec![self.into()])
187    }
188}
189
190impl IntoPropValue<VList> for ChildrenRenderer<VNode> {
191    #[inline]
192    fn into_prop_value(self) -> VList {
193        VList::from(self.children)
194    }
195}
196
197impl<C: BaseComponent> IntoPropValue<VList> for VChild<C> {
198    #[inline]
199    fn into_prop_value(self) -> VList {
200        VList::from(VNode::from(self))
201    }
202}
203
204impl IntoPropValue<ChildrenRenderer<VNode>> for AttrValue {
205    fn into_prop_value(self) -> ChildrenRenderer<VNode> {
206        ChildrenRenderer::new(vec![VNode::VText(VText::new(self))])
207    }
208}
209
210impl IntoPropValue<VNode> for Vec<VNode> {
211    #[inline]
212    fn into_prop_value(self) -> VNode {
213        VNode::VList(Rc::new(VList::from(self)))
214    }
215}
216
217impl<T: IntoPropValue<VNode>> IntoPropValue<VNode> for Option<T> {
218    #[inline]
219    fn into_prop_value(self) -> VNode {
220        self.map(IntoPropValue::into_prop_value).unwrap_or_default()
221    }
222}
223
224macro_rules! impl_into_prop {
225    (|$value:ident: $from_ty:ty| -> $to_ty:ty { $conversion:expr }) => {
226        // implement V -> T
227        impl IntoPropValue<$to_ty> for $from_ty {
228            #[inline]
229            fn into_prop_value(self) -> $to_ty {
230                let $value = self;
231                $conversion
232            }
233        }
234        // implement V -> Option<T>
235        impl IntoPropValue<Option<$to_ty>> for $from_ty {
236            #[inline]
237            fn into_prop_value(self) -> Option<$to_ty> {
238                let $value = self;
239                Some({ $conversion })
240            }
241        }
242        // implement Option<V> -> Option<T>
243        impl IntoPropValue<Option<$to_ty>> for Option<$from_ty> {
244            #[inline]
245            fn into_prop_value(self) -> Option<$to_ty> {
246                self.map(IntoPropValue::into_prop_value)
247            }
248        }
249    };
250}
251
252// implemented with literals in mind
253impl_into_prop!(|value: &'static str| -> String { value.to_owned() });
254impl_into_prop!(|value: &'static str| -> AttrValue { AttrValue::Static(value) });
255impl_into_prop!(|value: String| -> AttrValue { AttrValue::Rc(Rc::from(value)) });
256impl_into_prop!(|value: Rc<str>| -> AttrValue { AttrValue::Rc(value) });
257impl_into_prop!(|value: Cow<'static, str>| -> AttrValue { AttrValue::from(value) });
258impl_into_prop!(|value: &'static str| -> Cow<'static, str> { Cow::Borrowed(value) });
259impl_into_prop!(|value: String| -> Cow<'static, str> { Cow::Owned(value) });
260impl_into_prop!(|value: Rc<str>| -> Cow<'static, str> { Cow::Owned(value.to_string()) });
261
262impl<T: ImplicitClone + 'static> IntoPropValue<IArray<T>> for &'static [T] {
263    fn into_prop_value(self) -> IArray<T> {
264        IArray::from(self)
265    }
266}
267
268impl<T: ImplicitClone + 'static> IntoPropValue<IArray<T>> for Vec<T> {
269    fn into_prop_value(self) -> IArray<T> {
270        IArray::from(self)
271    }
272}
273
274impl<K: Eq + std::hash::Hash + ImplicitClone + 'static, V: PartialEq + ImplicitClone + 'static>
275    IntoPropValue<IMap<K, V>> for &'static [(K, V)]
276{
277    fn into_prop_value(self) -> IMap<K, V> {
278        IMap::from(self)
279    }
280}
281
282impl<K: Eq + std::hash::Hash + ImplicitClone + 'static, V: PartialEq + ImplicitClone + 'static>
283    IntoPropValue<IMap<K, V>> for indexmap::IndexMap<K, V>
284{
285    fn into_prop_value(self) -> IMap<K, V> {
286        IMap::from(self)
287    }
288}
289
290macro_rules! impl_into_prop_value_via_display {
291    ($from_ty: ty) => {
292        impl IntoPropValue<VNode> for $from_ty {
293            #[inline(always)]
294            fn into_prop_value(self) -> VNode {
295                VText::from(self).into()
296            }
297        }
298        impl IntoPropValue<VNode> for &$from_ty {
299            #[inline(always)]
300            fn into_prop_value(self) -> VNode {
301                self.clone().into_prop_value()
302            }
303        }
304        impl IntoPropValue<Option<VNode>> for $from_ty {
305            #[inline(always)]
306            fn into_prop_value(self) -> Option<VNode> {
307                Some(IntoPropValue::<VNode>::into_prop_value(self))
308            }
309        }
310        impl IntoPropValue<Option<VNode>> for &$from_ty {
311            #[inline(always)]
312            fn into_prop_value(self) -> Option<VNode> {
313                Some(IntoPropValue::<VNode>::into_prop_value(self))
314            }
315        }
316        impl IntoPropValue<Option<VNode>> for Option<$from_ty> {
317            #[inline(always)]
318            fn into_prop_value(self) -> Option<VNode> {
319                self.map(IntoPropValue::<VNode>::into_prop_value)
320            }
321        }
322    };
323}
324
325// go through AttrValue::from where possible
326macro_rules! impl_into_prop_value_via_attr_value {
327    ($from_ty: ty) => {
328        impl IntoPropValue<VNode> for $from_ty {
329            #[inline(always)]
330            fn into_prop_value(self) -> VNode {
331                VText::new(self).into()
332            }
333        }
334        impl IntoPropValue<Option<VNode>> for $from_ty {
335            #[inline(always)]
336            fn into_prop_value(self) -> Option<VNode> {
337                Some(VText::new(self).into())
338            }
339        }
340        impl IntoPropValue<Option<VNode>> for Option<$from_ty> {
341            #[inline(always)]
342            fn into_prop_value(self) -> Option<VNode> {
343                self.map(|v| VText::new(v).into())
344            }
345        }
346    };
347}
348
349// These are a selection of types implemented via display.
350impl_into_prop_value_via_display!(bool);
351impl_into_prop_value_via_display!(char);
352impl_into_prop_value_via_display!(&String);
353impl_into_prop_value_via_display!(&str);
354impl_into_prop_value_via_display!(Arc<str>);
355impl_into_prop_value_via_display!(Arc<String>);
356impl_into_prop_value_via_display!(Rc<String>);
357impl_into_prop_value_via_display!(u8);
358impl_into_prop_value_via_display!(u16);
359impl_into_prop_value_via_display!(u32);
360impl_into_prop_value_via_display!(u64);
361impl_into_prop_value_via_display!(u128);
362impl_into_prop_value_via_display!(usize);
363impl_into_prop_value_via_display!(i8);
364impl_into_prop_value_via_display!(i16);
365impl_into_prop_value_via_display!(i32);
366impl_into_prop_value_via_display!(i64);
367impl_into_prop_value_via_display!(i128);
368impl_into_prop_value_via_display!(isize);
369impl_into_prop_value_via_display!(f32);
370impl_into_prop_value_via_display!(f64);
371
372impl_into_prop_value_via_attr_value!(String);
373impl_into_prop_value_via_attr_value!(AttrValue);
374impl_into_prop_value_via_attr_value!(&AttrValue);
375impl_into_prop_value_via_attr_value!(Rc<str>);
376impl_into_prop_value_via_attr_value!(Cow<'static, str>);
377
378#[cfg(test)]
379mod test {
380    use super::*;
381
382    #[test]
383    fn test_str() {
384        let _: String = "foo".into_prop_value();
385        let _: Option<String> = "foo".into_prop_value();
386        let _: AttrValue = "foo".into_prop_value();
387        let _: Option<AttrValue> = "foo".into_prop_value();
388        let _: Option<AttrValue> = Rc::<str>::from("foo").into_prop_value();
389        let _: Option<AttrValue> = Cow::Borrowed("foo").into_prop_value();
390        let _: Cow<'static, str> = "foo".into_prop_value();
391        let _: Cow<'static, str> = String::from("foo").into_prop_value();
392        let _: Cow<'static, str> = Rc::<str>::from("foo").into_prop_value();
393        let _: Option<Cow<'static, str>> = Some("foo").into_prop_value();
394        let _: Option<Cow<'static, str>> = Some(String::from("foo")).into_prop_value();
395        let _: Option<Cow<'static, str>> = Some(Rc::<str>::from("foo")).into_prop_value();
396    }
397
398    #[test]
399    fn test_option_to_vnode() {
400        let _: VNode = Some(String::from("hello")).into_prop_value();
401        let _: VNode = Some(AttrValue::Static("hello")).into_prop_value();
402        let _: VNode = Option::<String>::None.into_prop_value();
403        let _: VNode = Option::<AttrValue>::None.into_prop_value();
404        let _: VNode = Some(VNode::default()).into_prop_value();
405        let _: VNode = Option::<VNode>::None.into_prop_value();
406        let _: VNode = Some(42u32).into_prop_value();
407        let _: VNode = Some(true).into_prop_value();
408    }
409
410    #[test]
411    fn test_into_option_vnode() {
412        // T -> Option<VNode>
413        let _: Option<VNode> = "hello".into_prop_value();
414        let _: Option<VNode> = String::from("hello").into_prop_value();
415        let _: Option<VNode> = AttrValue::Static("hello").into_prop_value();
416        let _: Option<VNode> = 42u32.into_prop_value();
417        let _: Option<VNode> = true.into_prop_value();
418        // &T -> Option<VNode>
419        let _: Option<VNode> = (&42u32).into_prop_value();
420        let _: Option<VNode> = (&true).into_prop_value();
421        let s = String::from("hello");
422        let _: Option<VNode> = (&s).into_prop_value();
423        // Option<T> -> Option<VNode>
424        let _: Option<VNode> = Some("hello").into_prop_value();
425        let _: Option<VNode> = Option::<&str>::None.into_prop_value();
426        let _: Option<VNode> = Some(String::from("hello")).into_prop_value();
427        let _: Option<VNode> = Option::<String>::None.into_prop_value();
428        let _: Option<VNode> = Some(42u32).into_prop_value();
429        let _: Option<VNode> = Some(true).into_prop_value();
430    }
431
432    #[test]
433    fn test_ref_to_vnode() {
434        let _: VNode = (&42i32).into_prop_value();
435        let _: VNode = (&true).into_prop_value();
436        let _: VNode = (&1.5f64).into_prop_value();
437        let s = String::from("hello");
438        let _: VNode = (&s).into_prop_value();
439        let a = AttrValue::Static("hello");
440        let _: VNode = (&a).into_prop_value();
441        let sr: &str = "hello";
442        let _: VNode = (&sr).into_prop_value();
443    }
444
445    #[test]
446    fn test_callback() {
447        let _: Callback<String> = (|_: String| ()).into_prop_value();
448        let _: Option<Callback<String>> = (|_: String| ()).into_prop_value();
449        let _: Option<Callback<String>> = Some(|_: String| ()).into_prop_value();
450        let _: Callback<String, String> = (|s: String| s).into_prop_value();
451        let _: Option<Callback<String, String>> = (|s: String| s).into_prop_value();
452        let _: Option<Callback<String, String>> = Some(|s: String| s).into_prop_value();
453    }
454
455    #[test]
456    fn test_html_to_children_compiles() {
457        use crate::prelude::*;
458
459        #[derive(Clone, Debug, PartialEq, Properties)]
460        pub struct Props {
461            #[prop_or_default]
462            pub header: Children,
463            #[prop_or_default]
464            pub children: Children,
465            #[prop_or_default]
466            pub footer: Children,
467        }
468
469        #[component]
470        pub fn App(props: &Props) -> Html {
471            let Props {
472                header,
473                children,
474                footer,
475            } = props.clone();
476
477            html! {
478                <div>
479                    <header>
480                        {header}
481                    </header>
482                    <main>
483                        {children}
484                    </main>
485                    <footer>
486                        {footer}
487                    </footer>
488                </div>
489            }
490        }
491
492        let header = html! { <div>{"header"}</div> };
493        let footer = html! { <div>{"footer"}</div> };
494        let children = html! { <div>{"main"}</div> };
495
496        let _ = html! {
497            <App {header} {footer}>
498                {children}
499            </App>
500        };
501    }
502
503    #[test]
504    fn test_vchild_to_children_with_props_compiles() {
505        use crate::prelude::*;
506
507        #[component]
508        pub fn Comp() -> Html {
509            Html::default()
510        }
511
512        #[derive(Clone, Debug, PartialEq, Properties)]
513        pub struct Props {
514            #[prop_or_default]
515            pub header: ChildrenWithProps<Comp>,
516            #[prop_or_default]
517            pub children: Children,
518            #[prop_or_default]
519            pub footer: ChildrenWithProps<Comp>,
520        }
521
522        #[component]
523        pub fn App(props: &Props) -> Html {
524            let Props {
525                header,
526                children,
527                footer,
528            } = props.clone();
529
530            html! {
531                <div>
532                    <header>
533                        {for header}
534                    </header>
535                    <main>
536                        {children}
537                    </main>
538                    <footer>
539                        {for footer}
540                    </footer>
541                </div>
542            }
543        }
544
545        let header = VChild::new((), None);
546        let footer = html_nested! { <Comp /> };
547        let children = html! { <div>{"main"}</div> };
548
549        let _ = html! {
550            <App {header} {footer}>
551                {children}
552            </App>
553        };
554    }
555
556    #[test]
557    fn test_vlist_to_children_compiles() {
558        use crate::prelude::*;
559        use crate::virtual_dom::VList;
560
561        #[component]
562        fn Foo() -> Html {
563            todo!()
564        }
565
566        #[derive(PartialEq, Properties)]
567        pub struct ChildProps {
568            #[prop_or_default]
569            pub children: Html,
570        }
571
572        #[component]
573        fn Child(_props: &ChildProps) -> Html {
574            html!()
575        }
576
577        #[derive(PartialEq, Properties)]
578        pub struct ParentProps {
579            pub children: VList,
580        }
581
582        #[component]
583        fn Parent(_props: &ParentProps) -> Html {
584            todo!()
585        }
586
587        let _ = html! {
588            <Parent>
589                <Child></Child>
590            </Parent>
591        };
592
593        let _ = html! {
594            <Parent>
595                <Child />
596                <Child />
597            </Parent>
598        };
599
600        let _ = html! {
601            <Parent>
602                <Child>
603                    <Foo />
604                </Child>
605            </Parent>
606        };
607    }
608
609    #[test]
610    fn attr_value_children() {
611        use crate::prelude::*;
612
613        #[derive(PartialEq, Properties)]
614        pub struct ChildProps {
615            #[prop_or_default]
616            pub children: AttrValue,
617        }
618
619        #[component]
620        fn Child(_props: &ChildProps) -> Html {
621            html!()
622        }
623        {
624            let attr_value = AttrValue::from("foo");
625
626            let _ = html! { <Child>{attr_value}</Child> };
627        }
628        {
629            let attr_value = AttrValue::from("foo");
630
631            let _ = html! { <Child>{&attr_value}</Child> };
632        }
633    }
634
635    #[test]
636    fn test_bare_none_option_string_prop() {
637        use crate::prelude::*;
638
639        #[derive(PartialEq, Properties)]
640        pub struct Props {
641            pub foo: Option<String>,
642        }
643
644        #[component]
645        fn Comp(_props: &Props) -> Html {
646            html! {}
647        }
648
649        let _ = html! { <Comp foo={None} /> };
650        let _ = html! { <Comp foo="hello" /> };
651        let _ = html! { <Comp foo={Some("hello")} /> };
652    }
653
654    #[test]
655    fn test_bare_none_option_attr_value_prop() {
656        use crate::prelude::*;
657
658        #[derive(PartialEq, Properties)]
659        pub struct Props {
660            pub foo: Option<AttrValue>,
661        }
662
663        #[component]
664        fn Comp(_props: &Props) -> Html {
665            html! {}
666        }
667
668        let _ = html! { <Comp foo={None} /> };
669        let _ = html! { <Comp foo="hello" /> };
670        let _ = html! { <Comp foo={AttrValue::from("hello")} /> };
671    }
672
673    #[test]
674    fn test_bare_none_option_html_prop() {
675        use crate::prelude::*;
676
677        #[derive(PartialEq, Properties)]
678        pub struct Props {
679            pub title: Option<Html>,
680        }
681
682        #[component]
683        fn Comp(_props: &Props) -> Html {
684            html! {}
685        }
686
687        let _ = html! { <Comp title={None} /> };
688        let _ = html! { <Comp title={Option::<Html>::None} /> };
689    }
690
691    #[test]
692    fn test_bare_none_optional_prop_with_default() {
693        use crate::prelude::*;
694
695        #[derive(PartialEq, Properties)]
696        pub struct Props {
697            #[prop_or_default]
698            pub foo: Option<String>,
699        }
700
701        #[component]
702        fn Comp(_props: &Props) -> Html {
703            html! {}
704        }
705
706        let _ = html! { <Comp foo={None} /> };
707        let _ = html! { <Comp foo="hello" /> };
708        let _ = html! { <Comp /> };
709    }
710
711    #[test]
712    fn test_option_html_prop_compiles() {
713        use crate::prelude::*;
714
715        #[derive(PartialEq, Properties)]
716        pub struct Props {
717            pub title: Option<Html>,
718        }
719
720        #[component]
721        fn Foo(props: &Props) -> Html {
722            match &props.title {
723                Some(title) => html! { <h1>{ title.clone() }</h1> },
724                None => html! {},
725            }
726        }
727
728        let _ = html! { <Foo title="Title" /> };
729
730        let _ = html! { <Foo title={String::from("Title")} /> };
731
732        let _ = html! { <Foo title={Some("Title")} /> };
733
734        let _ = html! { <Foo title={Option::<Html>::None} /> };
735    }
736}