yew/html/conversion/
into_prop_value.rs1use std::borrow::Cow;
2use std::rc::Rc;
3
4use implicit_clone::unsync::{IArray, IMap};
5pub use implicit_clone::ImplicitClone;
6
7use super::ToHtml;
8use crate::callback::Callback;
9use crate::html::{BaseComponent, ChildrenRenderer, Component, NodeRef, Scope};
10use crate::virtual_dom::{AttrValue, VChild, 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 T
134where
135 T: ToHtml,
136{
137 #[inline]
138 fn into_prop_value(self) -> VNode {
139 self.into_html()
140 }
141}
142
143impl IntoPropValue<ChildrenRenderer<VNode>> for VNode {
144 #[inline]
145 fn into_prop_value(self) -> ChildrenRenderer<VNode> {
146 ChildrenRenderer::new(vec![self])
147 }
148}
149
150impl IntoPropValue<ChildrenRenderer<VNode>> for VText {
151 #[inline]
152 fn into_prop_value(self) -> ChildrenRenderer<VNode> {
153 ChildrenRenderer::new(vec![self.into()])
154 }
155}
156
157macro_rules! impl_into_prop {
158 (|$value:ident: $from_ty:ty| -> $to_ty:ty { $conversion:expr }) => {
159 impl IntoPropValue<$to_ty> for $from_ty {
161 #[inline]
162 fn into_prop_value(self) -> $to_ty {
163 let $value = self;
164 $conversion
165 }
166 }
167 impl IntoPropValue<Option<$to_ty>> for $from_ty {
169 #[inline]
170 fn into_prop_value(self) -> Option<$to_ty> {
171 let $value = self;
172 Some({ $conversion })
173 }
174 }
175 impl IntoPropValue<Option<$to_ty>> for Option<$from_ty> {
177 #[inline]
178 fn into_prop_value(self) -> Option<$to_ty> {
179 self.map(IntoPropValue::into_prop_value)
180 }
181 }
182 };
183}
184
185impl_into_prop!(|value: &'static str| -> String { value.to_owned() });
187impl_into_prop!(|value: &'static str| -> AttrValue { AttrValue::Static(value) });
188impl_into_prop!(|value: String| -> AttrValue { AttrValue::Rc(Rc::from(value)) });
189impl_into_prop!(|value: Rc<str>| -> AttrValue { AttrValue::Rc(value) });
190impl_into_prop!(|value: Cow<'static, str>| -> AttrValue { AttrValue::from(value) });
191
192impl<T: ImplicitClone + 'static> IntoPropValue<IArray<T>> for &'static [T] {
193 fn into_prop_value(self) -> IArray<T> {
194 IArray::from(self)
195 }
196}
197
198impl<T: ImplicitClone + 'static> IntoPropValue<IArray<T>> for Vec<T> {
199 fn into_prop_value(self) -> IArray<T> {
200 IArray::from(self)
201 }
202}
203
204impl<K: Eq + std::hash::Hash + ImplicitClone + 'static, V: PartialEq + ImplicitClone + 'static>
205 IntoPropValue<IMap<K, V>> for &'static [(K, V)]
206{
207 fn into_prop_value(self) -> IMap<K, V> {
208 IMap::from(self)
209 }
210}
211
212impl<K: Eq + std::hash::Hash + ImplicitClone + 'static, V: PartialEq + ImplicitClone + 'static>
213 IntoPropValue<IMap<K, V>> for indexmap::IndexMap<K, V>
214{
215 fn into_prop_value(self) -> IMap<K, V> {
216 IMap::from(self)
217 }
218}
219
220#[cfg(test)]
221mod test {
222 use super::*;
223
224 #[test]
225 fn test_str() {
226 let _: String = "foo".into_prop_value();
227 let _: Option<String> = "foo".into_prop_value();
228 let _: AttrValue = "foo".into_prop_value();
229 let _: Option<AttrValue> = "foo".into_prop_value();
230 let _: Option<AttrValue> = Rc::<str>::from("foo").into_prop_value();
231 let _: Option<AttrValue> = Cow::Borrowed("foo").into_prop_value();
232 }
233
234 #[test]
235 fn test_callback() {
236 let _: Callback<String> = (|_: String| ()).into_prop_value();
237 let _: Option<Callback<String>> = (|_: String| ()).into_prop_value();
238 let _: Option<Callback<String>> = Some(|_: String| ()).into_prop_value();
239 let _: Callback<String, String> = (|s: String| s).into_prop_value();
240 let _: Option<Callback<String, String>> = (|s: String| s).into_prop_value();
241 let _: Option<Callback<String, String>> = Some(|s: String| s).into_prop_value();
242 }
243
244 #[test]
245 fn test_html_to_children_compiles() {
246 use crate::prelude::*;
247
248 #[derive(Clone, Debug, PartialEq, Properties)]
249 pub struct Props {
250 #[prop_or_default]
251 pub header: Children,
252 #[prop_or_default]
253 pub children: Children,
254 #[prop_or_default]
255 pub footer: Children,
256 }
257
258 #[function_component]
259 pub fn App(props: &Props) -> Html {
260 let Props {
261 header,
262 children,
263 footer,
264 } = props.clone();
265
266 html! {
267 <div>
268 <header>
269 {header}
270 </header>
271 <main>
272 {children}
273 </main>
274 <footer>
275 {footer}
276 </footer>
277 </div>
278 }
279 }
280
281 let header = html! { <div>{"header"}</div> };
282 let footer = html! { <div>{"footer"}</div> };
283 let children = html! { <div>{"main"}</div> };
284
285 let _ = html! {
286 <App {header} {footer}>
287 {children}
288 </App>
289 };
290 }
291
292 #[test]
293 fn test_vchild_to_children_with_props_compiles() {
294 use crate::prelude::*;
295
296 #[function_component]
297 pub fn Comp() -> Html {
298 Html::default()
299 }
300
301 #[derive(Clone, Debug, PartialEq, Properties)]
302 pub struct Props {
303 #[prop_or_default]
304 pub header: ChildrenWithProps<Comp>,
305 #[prop_or_default]
306 pub children: Children,
307 #[prop_or_default]
308 pub footer: ChildrenWithProps<Comp>,
309 }
310
311 #[function_component]
312 pub fn App(props: &Props) -> Html {
313 let Props {
314 header,
315 children,
316 footer,
317 } = props.clone();
318
319 html! {
320 <div>
321 <header>
322 {for header}
323 </header>
324 <main>
325 {children}
326 </main>
327 <footer>
328 {for footer}
329 </footer>
330 </div>
331 }
332 }
333
334 let header = VChild::new((), None);
335 let footer = html_nested! { <Comp /> };
336 let children = html! { <div>{"main"}</div> };
337
338 let _ = html! {
339 <App {header} {footer}>
340 {children}
341 </App>
342 };
343 }
344}