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 IntoPropValue<VNode> for Option<VNode> {
218    #[inline]
219    fn into_prop_value(self) -> VNode {
220        self.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    };
296}
297
298// go through AttrValue::from where possible
299macro_rules! impl_into_prop_value_via_attr_value {
300    ($from_ty: ty) => {
301        impl IntoPropValue<VNode> for $from_ty {
302            #[inline(always)]
303            fn into_prop_value(self) -> VNode {
304                VText::new(self).into()
305            }
306        }
307    };
308}
309
310// These are a selection of types implemented via display.
311impl_into_prop_value_via_display!(bool);
312impl_into_prop_value_via_display!(char);
313impl_into_prop_value_via_display!(&String);
314impl_into_prop_value_via_display!(&str);
315impl_into_prop_value_via_display!(Arc<str>);
316impl_into_prop_value_via_display!(Arc<String>);
317impl_into_prop_value_via_display!(Rc<String>);
318impl_into_prop_value_via_display!(u8);
319impl_into_prop_value_via_display!(u16);
320impl_into_prop_value_via_display!(u32);
321impl_into_prop_value_via_display!(u64);
322impl_into_prop_value_via_display!(u128);
323impl_into_prop_value_via_display!(usize);
324impl_into_prop_value_via_display!(i8);
325impl_into_prop_value_via_display!(i16);
326impl_into_prop_value_via_display!(i32);
327impl_into_prop_value_via_display!(i64);
328impl_into_prop_value_via_display!(i128);
329impl_into_prop_value_via_display!(isize);
330impl_into_prop_value_via_display!(f32);
331impl_into_prop_value_via_display!(f64);
332
333impl_into_prop_value_via_attr_value!(String);
334impl_into_prop_value_via_attr_value!(AttrValue);
335impl_into_prop_value_via_attr_value!(&AttrValue);
336impl_into_prop_value_via_attr_value!(Rc<str>);
337impl_into_prop_value_via_attr_value!(Cow<'static, str>);
338
339#[cfg(test)]
340mod test {
341    use super::*;
342
343    #[test]
344    fn test_str() {
345        let _: String = "foo".into_prop_value();
346        let _: Option<String> = "foo".into_prop_value();
347        let _: AttrValue = "foo".into_prop_value();
348        let _: Option<AttrValue> = "foo".into_prop_value();
349        let _: Option<AttrValue> = Rc::<str>::from("foo").into_prop_value();
350        let _: Option<AttrValue> = Cow::Borrowed("foo").into_prop_value();
351    }
352
353    #[test]
354    fn test_callback() {
355        let _: Callback<String> = (|_: String| ()).into_prop_value();
356        let _: Option<Callback<String>> = (|_: String| ()).into_prop_value();
357        let _: Option<Callback<String>> = Some(|_: String| ()).into_prop_value();
358        let _: Callback<String, String> = (|s: String| s).into_prop_value();
359        let _: Option<Callback<String, String>> = (|s: String| s).into_prop_value();
360        let _: Option<Callback<String, String>> = Some(|s: String| s).into_prop_value();
361    }
362
363    #[test]
364    fn test_html_to_children_compiles() {
365        use crate::prelude::*;
366
367        #[derive(Clone, Debug, PartialEq, Properties)]
368        pub struct Props {
369            #[prop_or_default]
370            pub header: Children,
371            #[prop_or_default]
372            pub children: Children,
373            #[prop_or_default]
374            pub footer: Children,
375        }
376
377        #[component]
378        pub fn App(props: &Props) -> Html {
379            let Props {
380                header,
381                children,
382                footer,
383            } = props.clone();
384
385            html! {
386                <div>
387                    <header>
388                        {header}
389                    </header>
390                    <main>
391                        {children}
392                    </main>
393                    <footer>
394                        {footer}
395                    </footer>
396                </div>
397            }
398        }
399
400        let header = html! { <div>{"header"}</div> };
401        let footer = html! { <div>{"footer"}</div> };
402        let children = html! { <div>{"main"}</div> };
403
404        let _ = html! {
405            <App {header} {footer}>
406                {children}
407            </App>
408        };
409    }
410
411    #[test]
412    fn test_vchild_to_children_with_props_compiles() {
413        use crate::prelude::*;
414
415        #[component]
416        pub fn Comp() -> Html {
417            Html::default()
418        }
419
420        #[derive(Clone, Debug, PartialEq, Properties)]
421        pub struct Props {
422            #[prop_or_default]
423            pub header: ChildrenWithProps<Comp>,
424            #[prop_or_default]
425            pub children: Children,
426            #[prop_or_default]
427            pub footer: ChildrenWithProps<Comp>,
428        }
429
430        #[component]
431        pub fn App(props: &Props) -> Html {
432            let Props {
433                header,
434                children,
435                footer,
436            } = props.clone();
437
438            html! {
439                <div>
440                    <header>
441                        {for header}
442                    </header>
443                    <main>
444                        {children}
445                    </main>
446                    <footer>
447                        {for footer}
448                    </footer>
449                </div>
450            }
451        }
452
453        let header = VChild::new((), None);
454        let footer = html_nested! { <Comp /> };
455        let children = html! { <div>{"main"}</div> };
456
457        let _ = html! {
458            <App {header} {footer}>
459                {children}
460            </App>
461        };
462    }
463
464    #[test]
465    fn test_vlist_to_children_compiles() {
466        use crate::prelude::*;
467        use crate::virtual_dom::VList;
468
469        #[component]
470        fn Foo() -> Html {
471            todo!()
472        }
473
474        #[derive(PartialEq, Properties)]
475        pub struct ChildProps {
476            #[prop_or_default]
477            pub children: Html,
478        }
479
480        #[component]
481        fn Child(_props: &ChildProps) -> Html {
482            html!()
483        }
484
485        #[derive(PartialEq, Properties)]
486        pub struct ParentProps {
487            pub children: VList,
488        }
489
490        #[component]
491        fn Parent(_props: &ParentProps) -> Html {
492            todo!()
493        }
494
495        let _ = html! {
496            <Parent>
497                <Child></Child>
498            </Parent>
499        };
500
501        let _ = html! {
502            <Parent>
503                <Child />
504                <Child />
505            </Parent>
506        };
507
508        let _ = html! {
509            <Parent>
510                <Child>
511                    <Foo />
512                </Child>
513            </Parent>
514        };
515    }
516
517    #[test]
518    fn attr_value_children() {
519        use crate::prelude::*;
520
521        #[derive(PartialEq, Properties)]
522        pub struct ChildProps {
523            #[prop_or_default]
524            pub children: AttrValue,
525        }
526
527        #[component]
528        fn Child(_props: &ChildProps) -> Html {
529            html!()
530        }
531        {
532            let attr_value = AttrValue::from("foo");
533
534            let _ = html! { <Child>{attr_value}</Child> };
535        }
536        {
537            let attr_value = AttrValue::from("foo");
538
539            let _ = html! { <Child>{&attr_value}</Child> };
540        }
541    }
542}