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, NodeRef, Scope};
10use crate::virtual_dom::{AttrValue, VChild, VList, VNode, VText};
11
12impl ImplicitClone for NodeRef {}
13impl<Comp: Component> ImplicitClone for Scope<Comp> {}
14pub trait IntoPropValue<T> {
18 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 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 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 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
246impl_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
292macro_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
304impl_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}