Skip to main content

topcoat_view/html/
node.rs

1use std::borrow::Cow;
2
3#[cfg(feature = "http")]
4use http::{HeaderMap, HeaderName, HeaderValue, StatusCode};
5use topcoat_core::context::Cx;
6
7use crate::{PartsWriter, PromotedStr, StaticStr, Unescaped, buffer::ViewHandle};
8
9/// Converts a value used in node position into view parts.
10///
11/// Implement this trait to interpolate a type as content in
12/// [`view!`](https://docs.rs/topcoat/latest/topcoat/view/macro.view.html):
13///
14/// ```rust
15/// # use topcoat::view::{View, component, view};
16/// # #[component]
17/// # async fn example() -> topcoat::Result<impl View> {
18/// # let my_value = "value";
19/// Ok(view! {
20///     <div>(my_value)</div>
21/// })
22/// # }
23/// ```
24pub trait NodeViewParts {
25    /// Appends this value to the view being built.
26    fn into_view_parts(self, cx: &Cx, parts: &mut PartsWriter<'_>);
27}
28
29/// Renders nothing.
30impl NodeViewParts for () {
31    #[inline]
32    fn into_view_parts(self, _cx: &Cx, _parts: &mut PartsWriter<'_>) {}
33}
34
35impl NodeViewParts for ViewHandle {
36    #[inline]
37    fn into_view_parts(self, _cx: &Cx, parts: &mut PartsWriter<'_>) {
38        parts.push_view_handle(self);
39    }
40}
41
42macro_rules! impl_primitive {
43    ($ty:ty, $method:ident) => {
44        impl NodeViewParts for $ty {
45            #[inline]
46            fn into_view_parts(self, _cx: &Cx, parts: &mut PartsWriter<'_>) {
47                parts.$method(self);
48            }
49        }
50    };
51    ($ty:ty, $method:ident, ref) => {
52        impl_primitive!($ty, $method);
53
54        impl NodeViewParts for &$ty {
55            #[inline]
56            fn into_view_parts(self, cx: &Cx, parts: &mut PartsWriter<'_>) {
57                (*self).into_view_parts(cx, parts)
58            }
59        }
60    };
61}
62
63impl_primitive!(bool, push_bool, ref);
64impl_primitive!(char, push_char, ref);
65impl_primitive!(i8, push_i8, ref);
66impl_primitive!(i16, push_i16, ref);
67impl_primitive!(i32, push_i32, ref);
68impl_primitive!(i64, push_i64, ref);
69impl_primitive!(i128, push_i128, ref);
70impl_primitive!(isize, push_isize, ref);
71impl_primitive!(u8, push_u8, ref);
72impl_primitive!(u16, push_u16, ref);
73impl_primitive!(u32, push_u32, ref);
74impl_primitive!(u64, push_u64, ref);
75impl_primitive!(u128, push_u128, ref);
76impl_primitive!(usize, push_usize, ref);
77impl_primitive!(f32, push_f32, ref);
78impl_primitive!(f64, push_f64, ref);
79impl_primitive!(String, push_string);
80
81impl NodeViewParts for Cow<'static, str> {
82    #[inline]
83    fn into_view_parts(self, _cx: &Cx, parts: &mut PartsWriter<'_>) {
84        match self {
85            Cow::Borrowed(value) => parts.push_static_str(value),
86            Cow::Owned(value) => parts.push_string(value),
87        };
88    }
89}
90
91impl NodeViewParts for &str {
92    #[inline]
93    fn into_view_parts(self, _cx: &Cx, parts: &mut PartsWriter<'_>) {
94        parts.push_str(self);
95    }
96}
97
98impl NodeViewParts for PromotedStr {
99    #[inline]
100    fn into_view_parts(self, _cx: &Cx, parts: &mut PartsWriter<'_>) {
101        parts.push_promoted_str(self.0);
102    }
103}
104
105impl NodeViewParts for StaticStr {
106    #[inline]
107    fn into_view_parts(self, _cx: &Cx, parts: &mut PartsWriter<'_>) {
108        parts.push_static_str(self.0);
109    }
110}
111
112impl NodeViewParts for Unescaped<String> {
113    #[inline]
114    fn into_view_parts(self, _cx: &Cx, parts: &mut PartsWriter<'_>) {
115        parts.push_string_unescaped(self.0);
116    }
117}
118
119impl NodeViewParts for Unescaped<PromotedStr> {
120    #[inline]
121    fn into_view_parts(self, _cx: &Cx, parts: &mut PartsWriter<'_>) {
122        parts.push_promoted_str_unescaped(self.0.0);
123    }
124}
125
126impl NodeViewParts for Unescaped<StaticStr> {
127    #[inline]
128    fn into_view_parts(self, _cx: &Cx, parts: &mut PartsWriter<'_>) {
129        parts.push_static_str_unescaped(self.0.0);
130    }
131}
132
133impl NodeViewParts for Unescaped<&'static str> {
134    #[inline]
135    fn into_view_parts(self, _cx: &Cx, parts: &mut PartsWriter<'_>) {
136        parts.push_static_str_unescaped(self.0);
137    }
138}
139
140impl NodeViewParts for &String {
141    #[inline]
142    fn into_view_parts(self, cx: &Cx, parts: &mut PartsWriter<'_>) {
143        self.as_str().into_view_parts(cx, parts);
144    }
145}
146
147/// Sets the response status code; renders no content.
148///
149/// Competing status codes resolve by render order: the first one rendered
150/// wins. Place a status code before nested content to override whatever the
151/// content declares, or after it to provide a fallback. To display a status
152/// code as text instead, render one of its accessors, such as
153/// [`as_u16`](StatusCode::as_u16).
154#[cfg(feature = "http")]
155impl NodeViewParts for StatusCode {
156    #[inline]
157    fn into_view_parts(self, _cx: &Cx, parts: &mut PartsWriter<'_>) {
158        parts.push_status_code(self);
159    }
160}
161
162/// Adds response headers; renders no content.
163///
164/// Competing headers resolve by render order: the first part that mentions a
165/// header name provides all of that name's values. Place headers before
166/// nested content to override the entries the content declares, or after it
167/// to provide fallbacks.
168#[cfg(feature = "http")]
169impl NodeViewParts for HeaderMap {
170    #[inline]
171    fn into_view_parts(self, _cx: &Cx, parts: &mut PartsWriter<'_>) {
172        parts.push_headers(self);
173    }
174}
175
176/// Adds a single response header; renders no content.
177///
178/// Equivalent to a [`HeaderMap`] holding just this entry.
179#[cfg(feature = "http")]
180impl NodeViewParts for (HeaderName, HeaderValue) {
181    fn into_view_parts(self, cx: &Cx, parts: &mut PartsWriter<'_>) {
182        let (name, value) = self;
183        let mut headers = HeaderMap::with_capacity(1);
184        headers.insert(name, value);
185        headers.into_view_parts(cx, parts);
186    }
187}
188
189impl<'b, T: ?Sized> NodeViewParts for &&'b T
190where
191    &'b T: NodeViewParts,
192{
193    #[inline]
194    fn into_view_parts(self, cx: &Cx, parts: &mut PartsWriter<'_>) {
195        (*self).into_view_parts(cx, parts);
196    }
197}
198
199impl<T> NodeViewParts for Option<T>
200where
201    T: NodeViewParts,
202{
203    #[inline]
204    fn into_view_parts(self, cx: &Cx, parts: &mut PartsWriter<'_>) {
205        if let Some(value) = self {
206            value.into_view_parts(cx, parts);
207        }
208    }
209}
210
211impl<T> NodeViewParts for Vec<T>
212where
213    T: NodeViewParts,
214{
215    #[inline]
216    fn into_view_parts(self, cx: &Cx, parts: &mut PartsWriter<'_>) {
217        for value in self {
218            value.into_view_parts(cx, parts);
219        }
220    }
221}
222
223macro_rules! impl_tuple {
224    ($($ty:ident),+) => {
225        impl<$($ty),+> NodeViewParts for ($($ty,)+)
226        where
227            $($ty: NodeViewParts,)+
228        {
229            #[inline]
230            #[allow(non_snake_case)]
231            fn into_view_parts(self, cx: &Cx, parts: &mut PartsWriter<'_>) {
232                let ($($ty,)+) = self;
233                $($ty.into_view_parts(cx, parts);)+
234            }
235        }
236    };
237}
238
239impl_tuple!(T1);
240impl_tuple!(T1, T2);
241impl_tuple!(T1, T2, T3);
242impl_tuple!(T1, T2, T3, T4);
243impl_tuple!(T1, T2, T3, T4, T5);
244impl_tuple!(T1, T2, T3, T4, T5, T6);
245impl_tuple!(T1, T2, T3, T4, T5, T6, T7);
246impl_tuple!(T1, T2, T3, T4, T5, T6, T7, T8);
247impl_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9);
248impl_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
249impl_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
250impl_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);