Skip to main content

leptos/
into_view.rs

1use std::borrow::Cow;
2use tachys::{
3    html::attribute::{Attribute, any_attribute::AnyAttribute},
4    hydration::Cursor,
5    ssr::StreamBuilder,
6    view::{
7        Position, PositionState, Render, RenderFlags, RenderHtml, ToTemplate,
8        add_attr::AddAnyAttr,
9    },
10};
11
12/// A wrapper for any kind of view.
13#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
14pub struct View<T>
15where
16    T: Sized,
17{
18    inner: T,
19    #[cfg(debug_assertions)]
20    view_marker: Option<Cow<'static, str>>,
21}
22
23impl<T> View<T> {
24    /// Wraps the view.
25    pub fn new(inner: T) -> Self {
26        Self {
27            inner,
28            #[cfg(debug_assertions)]
29            view_marker: None,
30        }
31    }
32
33    /// Unwraps the view, returning the inner type.
34    pub fn into_inner(self) -> T {
35        self.inner
36    }
37
38    /// Adds a view marker, which is used for hot-reloading and debug purposes.
39    #[inline(always)]
40    pub fn with_view_marker(
41        #[allow(unused_mut)] // used in debug
42        mut self,
43        #[allow(unused_variables)] // used in debug
44        view_marker: impl Into<Cow<'static, str>>,
45    ) -> Self {
46        #[cfg(debug_assertions)]
47        {
48            self.view_marker = Some(view_marker.into());
49        }
50        self
51    }
52}
53
54/// A trait that is implemented for types that can be rendered.
55pub trait IntoView
56where
57    Self: Sized + Render + RenderHtml + Send,
58{
59    /// Wraps the inner type.
60    fn into_view(self) -> View<Self>;
61}
62
63impl<T> IntoView for T
64where
65    T: Sized + Render + RenderHtml + Send, //+ AddAnyAttr,
66{
67    fn into_view(self) -> View<Self> {
68        View {
69            inner: self,
70            #[cfg(debug_assertions)]
71            view_marker: None,
72        }
73    }
74}
75
76impl<T: Render> Render for View<T> {
77    type State = T::State;
78
79    fn build(self) -> Self::State {
80        self.inner.build()
81    }
82
83    fn rebuild(self, state: &mut Self::State) {
84        self.inner.rebuild(state)
85    }
86}
87
88impl<T: RenderHtml> RenderHtml for View<T> {
89    type AsyncOutput = T::AsyncOutput;
90    type Owned = View<T::Owned>;
91
92    const MIN_LENGTH: usize = <T as RenderHtml>::MIN_LENGTH;
93    const EXISTS: bool = <T as RenderHtml>::EXISTS;
94
95    async fn resolve(self) -> Self::AsyncOutput {
96        self.inner.resolve().await
97    }
98
99    fn dry_resolve(&mut self) {
100        self.inner.dry_resolve();
101    }
102
103    fn to_html_with_buf(
104        self,
105        buf: &mut String,
106        position: &mut Position,
107        flags: RenderFlags,
108        extra_attrs: Vec<AnyAttribute>,
109    ) {
110        #[cfg(debug_assertions)]
111        let vm = if option_env!("LEPTOS_WATCH").is_some() {
112            self.view_marker.to_owned()
113        } else {
114            None
115        };
116
117        #[cfg(debug_assertions)]
118        if let Some(vm) = vm.as_ref() {
119            buf.push_str(&format!("<!--hot-reload|{vm}|open-->"));
120        }
121
122        self.inner
123            .to_html_with_buf(buf, position, flags, extra_attrs);
124
125        #[cfg(debug_assertions)]
126        if let Some(vm) = vm.as_ref() {
127            buf.push_str(&format!("<!--hot-reload|{vm}|close-->"));
128        }
129    }
130
131    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
132        self,
133        buf: &mut StreamBuilder,
134        position: &mut Position,
135        flags: RenderFlags,
136        extra_attrs: Vec<AnyAttribute>,
137    ) where
138        Self: Sized,
139    {
140        #[cfg(debug_assertions)]
141        let vm = if option_env!("LEPTOS_WATCH").is_some() {
142            self.view_marker.to_owned()
143        } else {
144            None
145        };
146
147        #[cfg(debug_assertions)]
148        if let Some(vm) = vm.as_ref() {
149            buf.push_sync(&format!("<!--hot-reload|{vm}|open-->"));
150        }
151
152        self.inner.to_html_async_with_buf::<OUT_OF_ORDER>(
153            buf,
154            position,
155            flags,
156            extra_attrs,
157        );
158
159        #[cfg(debug_assertions)]
160        if let Some(vm) = vm.as_ref() {
161            buf.push_sync(&format!("<!--hot-reload|{vm}|close-->"));
162        }
163    }
164
165    fn hydrate<const FROM_SERVER: bool>(
166        self,
167        cursor: &Cursor,
168        position: &PositionState,
169    ) -> Self::State {
170        self.inner.hydrate::<FROM_SERVER>(cursor, position)
171    }
172
173    async fn hydrate_async(
174        self,
175        cursor: &Cursor,
176        position: &PositionState,
177    ) -> Self::State {
178        self.inner.hydrate_async(cursor, position).await
179    }
180
181    fn into_owned(self) -> Self::Owned {
182        View {
183            inner: self.inner.into_owned(),
184            #[cfg(debug_assertions)]
185            view_marker: self.view_marker,
186        }
187    }
188}
189
190impl<T: ToTemplate> ToTemplate for View<T> {
191    fn to_template(
192        buf: &mut String,
193        class: &mut String,
194        style: &mut String,
195        inner_html: &mut String,
196        position: &mut Position,
197    ) {
198        T::to_template(buf, class, style, inner_html, position);
199    }
200}
201
202impl<T: AddAnyAttr> AddAnyAttr for View<T> {
203    type Output<SomeNewAttr: Attribute> = View<T::Output<SomeNewAttr>>;
204
205    fn add_any_attr<NewAttr: Attribute>(
206        self,
207        attr: NewAttr,
208    ) -> Self::Output<NewAttr>
209    where
210        Self::Output<NewAttr>: RenderHtml,
211    {
212        let View {
213            inner,
214            #[cfg(debug_assertions)]
215            view_marker,
216        } = self;
217        View {
218            inner: inner.add_any_attr(attr),
219            #[cfg(debug_assertions)]
220            view_marker,
221        }
222    }
223}
224
225/// Collects some iterator of views into a list, so they can be rendered.
226///
227/// This is a shorthand for `.collect::<Vec<_>>()`, and allows any iterator of renderable
228/// items to be collected into a renderable collection.
229pub trait CollectView {
230    /// The inner view type.
231    type View: IntoView;
232
233    /// Collects the iterator into a list of views.
234    fn collect_view(self) -> Vec<Self::View>;
235}
236
237impl<It, V> CollectView for It
238where
239    It: IntoIterator<Item = V>,
240    V: IntoView,
241{
242    type View = V;
243
244    fn collect_view(self) -> Vec<Self::View> {
245        self.into_iter().collect()
246    }
247}