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) });
258
259impl<T: ImplicitClone + 'static> IntoPropValue<IArray<T>> for &'static [T] {
260    fn into_prop_value(self) -> IArray<T> {
261        IArray::from(self)
262    }
263}
264
265impl<T: ImplicitClone + 'static> IntoPropValue<IArray<T>> for Vec<T> {
266    fn into_prop_value(self) -> IArray<T> {
267        IArray::from(self)
268    }
269}
270
271impl<K: Eq + std::hash::Hash + ImplicitClone + 'static, V: PartialEq + ImplicitClone + 'static>
272    IntoPropValue<IMap<K, V>> for &'static [(K, V)]
273{
274    fn into_prop_value(self) -> IMap<K, V> {
275        IMap::from(self)
276    }
277}
278
279impl<K: Eq + std::hash::Hash + ImplicitClone + 'static, V: PartialEq + ImplicitClone + 'static>
280    IntoPropValue<IMap<K, V>> for indexmap::IndexMap<K, V>
281{
282    fn into_prop_value(self) -> IMap<K, V> {
283        IMap::from(self)
284    }
285}
286
287macro_rules! impl_into_prop_value_via_display {
288    ($from_ty: ty) => {
289        impl IntoPropValue<VNode> for $from_ty {
290            #[inline(always)]
291            fn into_prop_value(self) -> VNode {
292                VText::from(self).into()
293            }
294        }
295        impl IntoPropValue<VNode> for &$from_ty {
296            #[inline(always)]
297            fn into_prop_value(self) -> VNode {
298                self.clone().into_prop_value()
299            }
300        }
301        impl IntoPropValue<Option<VNode>> for $from_ty {
302            #[inline(always)]
303            fn into_prop_value(self) -> Option<VNode> {
304                Some(IntoPropValue::<VNode>::into_prop_value(self))
305            }
306        }
307        impl IntoPropValue<Option<VNode>> for &$from_ty {
308            #[inline(always)]
309            fn into_prop_value(self) -> Option<VNode> {
310                Some(IntoPropValue::<VNode>::into_prop_value(self))
311            }
312        }
313        impl IntoPropValue<Option<VNode>> for Option<$from_ty> {
314            #[inline(always)]
315            fn into_prop_value(self) -> Option<VNode> {
316                self.map(IntoPropValue::<VNode>::into_prop_value)
317            }
318        }
319    };
320}
321
322// go through AttrValue::from where possible
323macro_rules! impl_into_prop_value_via_attr_value {
324    ($from_ty: ty) => {
325        impl IntoPropValue<VNode> for $from_ty {
326            #[inline(always)]
327            fn into_prop_value(self) -> VNode {
328                VText::new(self).into()
329            }
330        }
331        impl IntoPropValue<Option<VNode>> for $from_ty {
332            #[inline(always)]
333            fn into_prop_value(self) -> Option<VNode> {
334                Some(VText::new(self).into())
335            }
336        }
337        impl IntoPropValue<Option<VNode>> for Option<$from_ty> {
338            #[inline(always)]
339            fn into_prop_value(self) -> Option<VNode> {
340                self.map(|v| VText::new(v).into())
341            }
342        }
343    };
344}
345
346// These are a selection of types implemented via display.
347impl_into_prop_value_via_display!(bool);
348impl_into_prop_value_via_display!(char);
349impl_into_prop_value_via_display!(&String);
350impl_into_prop_value_via_display!(&str);
351impl_into_prop_value_via_display!(Arc<str>);
352impl_into_prop_value_via_display!(Arc<String>);
353impl_into_prop_value_via_display!(Rc<String>);
354impl_into_prop_value_via_display!(u8);
355impl_into_prop_value_via_display!(u16);
356impl_into_prop_value_via_display!(u32);
357impl_into_prop_value_via_display!(u64);
358impl_into_prop_value_via_display!(u128);
359impl_into_prop_value_via_display!(usize);
360impl_into_prop_value_via_display!(i8);
361impl_into_prop_value_via_display!(i16);
362impl_into_prop_value_via_display!(i32);
363impl_into_prop_value_via_display!(i64);
364impl_into_prop_value_via_display!(i128);
365impl_into_prop_value_via_display!(isize);
366impl_into_prop_value_via_display!(f32);
367impl_into_prop_value_via_display!(f64);
368
369impl_into_prop_value_via_attr_value!(String);
370impl_into_prop_value_via_attr_value!(AttrValue);
371impl_into_prop_value_via_attr_value!(&AttrValue);
372impl_into_prop_value_via_attr_value!(Rc<str>);
373impl_into_prop_value_via_attr_value!(Cow<'static, str>);
374
375#[cfg(test)]
376mod test {
377    use super::*;
378
379    #[test]
380    fn test_str() {
381        let _: String = "foo".into_prop_value();
382        let _: Option<String> = "foo".into_prop_value();
383        let _: AttrValue = "foo".into_prop_value();
384        let _: Option<AttrValue> = "foo".into_prop_value();
385        let _: Option<AttrValue> = Rc::<str>::from("foo").into_prop_value();
386        let _: Option<AttrValue> = Cow::Borrowed("foo").into_prop_value();
387    }
388
389    #[test]
390    fn test_option_to_vnode() {
391        let _: VNode = Some(String::from("hello")).into_prop_value();
392        let _: VNode = Some(AttrValue::Static("hello")).into_prop_value();
393        let _: VNode = Option::<String>::None.into_prop_value();
394        let _: VNode = Option::<AttrValue>::None.into_prop_value();
395        let _: VNode = Some(VNode::default()).into_prop_value();
396        let _: VNode = Option::<VNode>::None.into_prop_value();
397        let _: VNode = Some(42u32).into_prop_value();
398        let _: VNode = Some(true).into_prop_value();
399    }
400
401    #[test]
402    fn test_into_option_vnode() {
403        // T -> Option<VNode>
404        let _: Option<VNode> = "hello".into_prop_value();
405        let _: Option<VNode> = String::from("hello").into_prop_value();
406        let _: Option<VNode> = AttrValue::Static("hello").into_prop_value();
407        let _: Option<VNode> = 42u32.into_prop_value();
408        let _: Option<VNode> = true.into_prop_value();
409        // &T -> Option<VNode>
410        let _: Option<VNode> = (&42u32).into_prop_value();
411        let _: Option<VNode> = (&true).into_prop_value();
412        let s = String::from("hello");
413        let _: Option<VNode> = (&s).into_prop_value();
414        // Option<T> -> Option<VNode>
415        let _: Option<VNode> = Some("hello").into_prop_value();
416        let _: Option<VNode> = Option::<&str>::None.into_prop_value();
417        let _: Option<VNode> = Some(String::from("hello")).into_prop_value();
418        let _: Option<VNode> = Option::<String>::None.into_prop_value();
419        let _: Option<VNode> = Some(42u32).into_prop_value();
420        let _: Option<VNode> = Some(true).into_prop_value();
421    }
422
423    #[test]
424    fn test_ref_to_vnode() {
425        let _: VNode = (&42i32).into_prop_value();
426        let _: VNode = (&true).into_prop_value();
427        let _: VNode = (&1.5f64).into_prop_value();
428        let s = String::from("hello");
429        let _: VNode = (&s).into_prop_value();
430        let a = AttrValue::Static("hello");
431        let _: VNode = (&a).into_prop_value();
432        let sr: &str = "hello";
433        let _: VNode = (&sr).into_prop_value();
434    }
435
436    #[test]
437    fn test_callback() {
438        let _: Callback<String> = (|_: String| ()).into_prop_value();
439        let _: Option<Callback<String>> = (|_: String| ()).into_prop_value();
440        let _: Option<Callback<String>> = Some(|_: String| ()).into_prop_value();
441        let _: Callback<String, String> = (|s: String| s).into_prop_value();
442        let _: Option<Callback<String, String>> = (|s: String| s).into_prop_value();
443        let _: Option<Callback<String, String>> = Some(|s: String| s).into_prop_value();
444    }
445
446    #[test]
447    fn test_html_to_children_compiles() {
448        use crate::prelude::*;
449
450        #[derive(Clone, Debug, PartialEq, Properties)]
451        pub struct Props {
452            #[prop_or_default]
453            pub header: Children,
454            #[prop_or_default]
455            pub children: Children,
456            #[prop_or_default]
457            pub footer: Children,
458        }
459
460        #[component]
461        pub fn App(props: &Props) -> Html {
462            let Props {
463                header,
464                children,
465                footer,
466            } = props.clone();
467
468            html! {
469                <div>
470                    <header>
471                        {header}
472                    </header>
473                    <main>
474                        {children}
475                    </main>
476                    <footer>
477                        {footer}
478                    </footer>
479                </div>
480            }
481        }
482
483        let header = html! { <div>{"header"}</div> };
484        let footer = html! { <div>{"footer"}</div> };
485        let children = html! { <div>{"main"}</div> };
486
487        let _ = html! {
488            <App {header} {footer}>
489                {children}
490            </App>
491        };
492    }
493
494    #[test]
495    fn test_vchild_to_children_with_props_compiles() {
496        use crate::prelude::*;
497
498        #[component]
499        pub fn Comp() -> Html {
500            Html::default()
501        }
502
503        #[derive(Clone, Debug, PartialEq, Properties)]
504        pub struct Props {
505            #[prop_or_default]
506            pub header: ChildrenWithProps<Comp>,
507            #[prop_or_default]
508            pub children: Children,
509            #[prop_or_default]
510            pub footer: ChildrenWithProps<Comp>,
511        }
512
513        #[component]
514        pub fn App(props: &Props) -> Html {
515            let Props {
516                header,
517                children,
518                footer,
519            } = props.clone();
520
521            html! {
522                <div>
523                    <header>
524                        {for header}
525                    </header>
526                    <main>
527                        {children}
528                    </main>
529                    <footer>
530                        {for footer}
531                    </footer>
532                </div>
533            }
534        }
535
536        let header = VChild::new((), None);
537        let footer = html_nested! { <Comp /> };
538        let children = html! { <div>{"main"}</div> };
539
540        let _ = html! {
541            <App {header} {footer}>
542                {children}
543            </App>
544        };
545    }
546
547    #[test]
548    fn test_vlist_to_children_compiles() {
549        use crate::prelude::*;
550        use crate::virtual_dom::VList;
551
552        #[component]
553        fn Foo() -> Html {
554            todo!()
555        }
556
557        #[derive(PartialEq, Properties)]
558        pub struct ChildProps {
559            #[prop_or_default]
560            pub children: Html,
561        }
562
563        #[component]
564        fn Child(_props: &ChildProps) -> Html {
565            html!()
566        }
567
568        #[derive(PartialEq, Properties)]
569        pub struct ParentProps {
570            pub children: VList,
571        }
572
573        #[component]
574        fn Parent(_props: &ParentProps) -> Html {
575            todo!()
576        }
577
578        let _ = html! {
579            <Parent>
580                <Child></Child>
581            </Parent>
582        };
583
584        let _ = html! {
585            <Parent>
586                <Child />
587                <Child />
588            </Parent>
589        };
590
591        let _ = html! {
592            <Parent>
593                <Child>
594                    <Foo />
595                </Child>
596            </Parent>
597        };
598    }
599
600    #[test]
601    fn attr_value_children() {
602        use crate::prelude::*;
603
604        #[derive(PartialEq, Properties)]
605        pub struct ChildProps {
606            #[prop_or_default]
607            pub children: AttrValue,
608        }
609
610        #[component]
611        fn Child(_props: &ChildProps) -> Html {
612            html!()
613        }
614        {
615            let attr_value = AttrValue::from("foo");
616
617            let _ = html! { <Child>{attr_value}</Child> };
618        }
619        {
620            let attr_value = AttrValue::from("foo");
621
622            let _ = html! { <Child>{&attr_value}</Child> };
623        }
624    }
625
626    #[test]
627    fn test_option_html_prop_compiles() {
628        use crate::prelude::*;
629
630        #[derive(PartialEq, Properties)]
631        pub struct Props {
632            pub title: Option<Html>,
633        }
634
635        #[component]
636        fn Foo(props: &Props) -> Html {
637            match &props.title {
638                Some(title) => html! { <h1>{ title.clone() }</h1> },
639                None => html! {},
640            }
641        }
642
643        let _ = html! { <Foo title="Title" /> };
644
645        let _ = html! { <Foo title={String::from("Title")} /> };
646
647        let _ = html! { <Foo title={Some("Title")} /> };
648
649        let _ = html! { <Foo title={Option::<Html>::None} /> };
650    }
651}