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