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 };
302}
303
304macro_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
316impl_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}