yew/html/conversion/
into_prop_value.rs1use 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> {}
13pub trait IntoPropValue<T> {
17 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 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 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 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
252impl_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
322macro_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
346impl_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 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 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 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}