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