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