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, NodeRef, Scope};
10use crate::virtual_dom::{AttrValue, VChild, VList, VNode, VText};
11
12impl ImplicitClone for NodeRef {}
13impl<Comp: Component> ImplicitClone for Scope<Comp> {}
14// TODO there are still a few missing
15
16/// A trait similar to `Into<T>` which allows conversion to a value of a `Properties` struct.
17pub trait IntoPropValue<T> {
18    /// Convert `self` to a value of a `Properties` struct.
19    fn into_prop_value(self) -> T;
20}
21
22impl<T> IntoPropValue<T> for T {
23    #[inline]
24    fn into_prop_value(self) -> T {
25        self
26    }
27}
28
29impl<T> IntoPropValue<T> for &T
30where
31    T: ImplicitClone,
32{
33    #[inline]
34    fn into_prop_value(self) -> T {
35        self.clone()
36    }
37}
38
39impl<T> IntoPropValue<Option<T>> for T {
40    #[inline]
41    fn into_prop_value(self) -> Option<T> {
42        Some(self)
43    }
44}
45
46impl<T> IntoPropValue<Option<T>> for &T
47where
48    T: ImplicitClone,
49{
50    #[inline]
51    fn into_prop_value(self) -> Option<T> {
52        Some(self.clone())
53    }
54}
55
56impl<I, O, F> IntoPropValue<Callback<I, O>> for F
57where
58    F: 'static + Fn(I) -> O,
59{
60    #[inline]
61    fn into_prop_value(self) -> Callback<I, O> {
62        Callback::from(self)
63    }
64}
65
66impl<I, O, F> IntoPropValue<Option<Callback<I, O>>> for F
67where
68    F: 'static + Fn(I) -> O,
69{
70    #[inline]
71    fn into_prop_value(self) -> Option<Callback<I, O>> {
72        Some(Callback::from(self))
73    }
74}
75
76impl<I, O, F> IntoPropValue<Option<Callback<I, O>>> for Option<F>
77where
78    F: 'static + Fn(I) -> O,
79{
80    #[inline]
81    fn into_prop_value(self) -> Option<Callback<I, O>> {
82        self.map(Callback::from)
83    }
84}
85
86impl<T, C> IntoPropValue<ChildrenRenderer<C>> for VChild<T>
87where
88    T: BaseComponent,
89    C: Clone + Into<VNode>,
90    VChild<T>: Into<C>,
91{
92    #[inline]
93    fn into_prop_value(self) -> ChildrenRenderer<C> {
94        ChildrenRenderer::new(vec![self.into()])
95    }
96}
97
98impl<T, C> IntoPropValue<Option<ChildrenRenderer<C>>> for VChild<T>
99where
100    T: BaseComponent,
101    C: Clone + Into<VNode>,
102    VChild<T>: Into<C>,
103{
104    #[inline]
105    fn into_prop_value(self) -> Option<ChildrenRenderer<C>> {
106        Some(ChildrenRenderer::new(vec![self.into()]))
107    }
108}
109
110impl<T, C> IntoPropValue<Option<ChildrenRenderer<C>>> for Option<VChild<T>>
111where
112    T: BaseComponent,
113    C: Clone + Into<VNode>,
114    VChild<T>: Into<C>,
115{
116    #[inline]
117    fn into_prop_value(self) -> Option<ChildrenRenderer<C>> {
118        self.map(|m| ChildrenRenderer::new(vec![m.into()]))
119    }
120}
121
122impl<T, R> IntoPropValue<ChildrenRenderer<R>> for Vec<T>
123where
124    T: Into<R>,
125    R: Clone + Into<VNode>,
126{
127    #[inline]
128    fn into_prop_value(self) -> ChildrenRenderer<R> {
129        ChildrenRenderer::new(self.into_iter().map(|m| m.into()).collect())
130    }
131}
132
133impl<T> IntoPropValue<VNode> for VChild<T>
134where
135    T: BaseComponent,
136{
137    #[inline]
138    fn into_prop_value(self) -> VNode {
139        VNode::from(self)
140    }
141}
142
143impl IntoPropValue<VNode> for VList {
144    #[inline]
145    fn into_prop_value(self) -> VNode {
146        VNode::VList(Rc::new(self))
147    }
148}
149impl IntoPropValue<VNode> for VText {
150    #[inline]
151    fn into_prop_value(self) -> VNode {
152        VNode::VText(self)
153    }
154}
155
156impl IntoPropValue<VNode> for () {
157    #[inline]
158    fn into_prop_value(self) -> VNode {
159        VNode::default()
160    }
161}
162
163impl IntoPropValue<VNode> for ChildrenRenderer<VNode> {
164    #[inline]
165    fn into_prop_value(self) -> VNode {
166        VNode::VList(Rc::new(self.into()))
167    }
168}
169
170impl IntoPropValue<ChildrenRenderer<VNode>> for VNode {
171    #[inline]
172    fn into_prop_value(self) -> ChildrenRenderer<VNode> {
173        ChildrenRenderer::new(vec![self])
174    }
175}
176
177impl IntoPropValue<ChildrenRenderer<VNode>> for VText {
178    #[inline]
179    fn into_prop_value(self) -> ChildrenRenderer<VNode> {
180        ChildrenRenderer::new(vec![self.into()])
181    }
182}
183
184impl IntoPropValue<VList> for ChildrenRenderer<VNode> {
185    #[inline]
186    fn into_prop_value(self) -> VList {
187        VList::with_children(self.children, None)
188    }
189}
190
191impl<C: BaseComponent> IntoPropValue<VList> for VChild<C> {
192    #[inline]
193    fn into_prop_value(self) -> VList {
194        VList::with_children(vec![self.into()], None)
195    }
196}
197
198impl IntoPropValue<ChildrenRenderer<VNode>> for AttrValue {
199    fn into_prop_value(self) -> ChildrenRenderer<VNode> {
200        ChildrenRenderer::new(vec![VNode::VText(VText::new(self))])
201    }
202}
203
204impl IntoPropValue<VNode> for Vec<VNode> {
205    #[inline]
206    fn into_prop_value(self) -> VNode {
207        VNode::VList(Rc::new(VList::with_children(self, None)))
208    }
209}
210
211impl IntoPropValue<VNode> for Option<VNode> {
212    #[inline]
213    fn into_prop_value(self) -> VNode {
214        self.unwrap_or_default()
215    }
216}
217
218macro_rules! impl_into_prop {
219    (|$value:ident: $from_ty:ty| -> $to_ty:ty { $conversion:expr }) => {
220        // implement V -> T
221        impl IntoPropValue<$to_ty> for $from_ty {
222            #[inline]
223            fn into_prop_value(self) -> $to_ty {
224                let $value = self;
225                $conversion
226            }
227        }
228        // implement V -> Option<T>
229        impl IntoPropValue<Option<$to_ty>> for $from_ty {
230            #[inline]
231            fn into_prop_value(self) -> Option<$to_ty> {
232                let $value = self;
233                Some({ $conversion })
234            }
235        }
236        // implement Option<V> -> Option<T>
237        impl IntoPropValue<Option<$to_ty>> for Option<$from_ty> {
238            #[inline]
239            fn into_prop_value(self) -> Option<$to_ty> {
240                self.map(IntoPropValue::into_prop_value)
241            }
242        }
243    };
244}
245
246// implemented with literals in mind
247impl_into_prop!(|value: &'static str| -> String { value.to_owned() });
248impl_into_prop!(|value: &'static str| -> AttrValue { AttrValue::Static(value) });
249impl_into_prop!(|value: String| -> AttrValue { AttrValue::Rc(Rc::from(value)) });
250impl_into_prop!(|value: Rc<str>| -> AttrValue { AttrValue::Rc(value) });
251impl_into_prop!(|value: Cow<'static, str>| -> AttrValue { AttrValue::from(value) });
252
253impl<T: ImplicitClone + 'static> IntoPropValue<IArray<T>> for &'static [T] {
254    fn into_prop_value(self) -> IArray<T> {
255        IArray::from(self)
256    }
257}
258
259impl<T: ImplicitClone + 'static> IntoPropValue<IArray<T>> for Vec<T> {
260    fn into_prop_value(self) -> IArray<T> {
261        IArray::from(self)
262    }
263}
264
265impl<K: Eq + std::hash::Hash + ImplicitClone + 'static, V: PartialEq + ImplicitClone + 'static>
266    IntoPropValue<IMap<K, V>> for &'static [(K, V)]
267{
268    fn into_prop_value(self) -> IMap<K, V> {
269        IMap::from(self)
270    }
271}
272
273impl<K: Eq + std::hash::Hash + ImplicitClone + 'static, V: PartialEq + ImplicitClone + 'static>
274    IntoPropValue<IMap<K, V>> for indexmap::IndexMap<K, V>
275{
276    fn into_prop_value(self) -> IMap<K, V> {
277        IMap::from(self)
278    }
279}
280
281macro_rules! impl_into_prop_value_via_display {
282    ($from_ty: ty) => {
283        impl IntoPropValue<VNode> for $from_ty {
284            #[inline(always)]
285            fn into_prop_value(self) -> VNode {
286                VText::from(self).into()
287            }
288        }
289    };
290}
291
292// go through AttrValue::from where possible
293macro_rules! impl_into_prop_value_via_attr_value {
294    ($from_ty: ty) => {
295        impl IntoPropValue<VNode> for $from_ty {
296            #[inline(always)]
297            fn into_prop_value(self) -> VNode {
298                VText::new(self).into()
299            }
300        }
301    };
302}
303
304// These are a selection of types implemented via display.
305impl_into_prop_value_via_display!(bool);
306impl_into_prop_value_via_display!(char);
307impl_into_prop_value_via_display!(&String);
308impl_into_prop_value_via_display!(&str);
309impl_into_prop_value_via_display!(Arc<str>);
310impl_into_prop_value_via_display!(Arc<String>);
311impl_into_prop_value_via_display!(Rc<String>);
312impl_into_prop_value_via_display!(u8);
313impl_into_prop_value_via_display!(u16);
314impl_into_prop_value_via_display!(u32);
315impl_into_prop_value_via_display!(u64);
316impl_into_prop_value_via_display!(u128);
317impl_into_prop_value_via_display!(usize);
318impl_into_prop_value_via_display!(i8);
319impl_into_prop_value_via_display!(i16);
320impl_into_prop_value_via_display!(i32);
321impl_into_prop_value_via_display!(i64);
322impl_into_prop_value_via_display!(i128);
323impl_into_prop_value_via_display!(isize);
324impl_into_prop_value_via_display!(f32);
325impl_into_prop_value_via_display!(f64);
326
327impl_into_prop_value_via_attr_value!(String);
328impl_into_prop_value_via_attr_value!(AttrValue);
329impl_into_prop_value_via_attr_value!(Rc<str>);
330impl_into_prop_value_via_attr_value!(Cow<'static, str>);
331
332#[cfg(test)]
333mod test {
334    use super::*;
335
336    #[test]
337    fn test_str() {
338        let _: String = "foo".into_prop_value();
339        let _: Option<String> = "foo".into_prop_value();
340        let _: AttrValue = "foo".into_prop_value();
341        let _: Option<AttrValue> = "foo".into_prop_value();
342        let _: Option<AttrValue> = Rc::<str>::from("foo").into_prop_value();
343        let _: Option<AttrValue> = Cow::Borrowed("foo").into_prop_value();
344    }
345
346    #[test]
347    fn test_callback() {
348        let _: Callback<String> = (|_: String| ()).into_prop_value();
349        let _: Option<Callback<String>> = (|_: String| ()).into_prop_value();
350        let _: Option<Callback<String>> = Some(|_: String| ()).into_prop_value();
351        let _: Callback<String, String> = (|s: String| s).into_prop_value();
352        let _: Option<Callback<String, String>> = (|s: String| s).into_prop_value();
353        let _: Option<Callback<String, String>> = Some(|s: String| s).into_prop_value();
354    }
355
356    #[test]
357    fn test_html_to_children_compiles() {
358        use crate::prelude::*;
359
360        #[derive(Clone, Debug, PartialEq, Properties)]
361        pub struct Props {
362            #[prop_or_default]
363            pub header: Children,
364            #[prop_or_default]
365            pub children: Children,
366            #[prop_or_default]
367            pub footer: Children,
368        }
369
370        #[function_component]
371        pub fn App(props: &Props) -> Html {
372            let Props {
373                header,
374                children,
375                footer,
376            } = props.clone();
377
378            html! {
379                <div>
380                    <header>
381                        {header}
382                    </header>
383                    <main>
384                        {children}
385                    </main>
386                    <footer>
387                        {footer}
388                    </footer>
389                </div>
390            }
391        }
392
393        let header = html! { <div>{"header"}</div> };
394        let footer = html! { <div>{"footer"}</div> };
395        let children = html! { <div>{"main"}</div> };
396
397        let _ = html! {
398            <App {header} {footer}>
399                {children}
400            </App>
401        };
402    }
403
404    #[test]
405    fn test_vchild_to_children_with_props_compiles() {
406        use crate::prelude::*;
407
408        #[function_component]
409        pub fn Comp() -> Html {
410            Html::default()
411        }
412
413        #[derive(Clone, Debug, PartialEq, Properties)]
414        pub struct Props {
415            #[prop_or_default]
416            pub header: ChildrenWithProps<Comp>,
417            #[prop_or_default]
418            pub children: Children,
419            #[prop_or_default]
420            pub footer: ChildrenWithProps<Comp>,
421        }
422
423        #[function_component]
424        pub fn App(props: &Props) -> Html {
425            let Props {
426                header,
427                children,
428                footer,
429            } = props.clone();
430
431            html! {
432                <div>
433                    <header>
434                        {for header}
435                    </header>
436                    <main>
437                        {children}
438                    </main>
439                    <footer>
440                        {for footer}
441                    </footer>
442                </div>
443            }
444        }
445
446        let header = VChild::new((), None);
447        let footer = html_nested! { <Comp /> };
448        let children = html! { <div>{"main"}</div> };
449
450        let _ = html! {
451            <App {header} {footer}>
452                {children}
453            </App>
454        };
455    }
456
457    #[test]
458    fn test_vlist_to_children_compiles() {
459        use crate::prelude::*;
460        use crate::virtual_dom::VList;
461
462        #[function_component]
463        fn Foo() -> Html {
464            todo!()
465        }
466
467        #[derive(PartialEq, Properties)]
468        pub struct ChildProps {
469            #[prop_or_default]
470            pub children: Html,
471        }
472
473        #[function_component]
474        fn Child(_props: &ChildProps) -> Html {
475            html!()
476        }
477
478        #[derive(PartialEq, Properties)]
479        pub struct ParentProps {
480            pub children: VList,
481        }
482
483        #[function_component]
484        fn Parent(_props: &ParentProps) -> Html {
485            todo!()
486        }
487
488        let _ = html! {
489            <Parent>
490                <Child></Child>
491            </Parent>
492        };
493
494        let _ = html! {
495            <Parent>
496                <Child />
497                <Child />
498            </Parent>
499        };
500
501        let _ = html! {
502            <Parent>
503                <Child>
504                    <Foo />
505                </Child>
506            </Parent>
507        };
508    }
509
510    #[test]
511    fn attr_value_children() {
512        use crate::prelude::*;
513
514        #[derive(PartialEq, Properties)]
515        pub struct ChildProps {
516            #[prop_or_default]
517            pub children: AttrValue,
518        }
519
520        #[function_component]
521        fn Child(_props: &ChildProps) -> Html {
522            html!()
523        }
524        {
525            let attr_value = AttrValue::from("foo");
526
527            let _ = html! { <Child>{attr_value}</Child> };
528        }
529        {
530            let attr_value = AttrValue::from("foo");
531
532            let _ = html! { <Child>{&attr_value}</Child> };
533        }
534    }
535}