leptos/
into_view.rs

1use std::borrow::Cow;
2use tachys::{
3    html::attribute::{any_attribute::AnyAttribute, Attribute},
4    hydration::Cursor,
5    ssr::StreamBuilder,
6    view::{
7        add_attr::AddAnyAttr, Position, PositionState, Render, RenderHtml,
8        ToTemplate,
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        escape: bool,
108        mark_branches: bool,
109        extra_attrs: Vec<AnyAttribute>,
110    ) {
111        #[cfg(debug_assertions)]
112        let vm = if option_env!("LEPTOS_WATCH").is_some() {
113            self.view_marker.to_owned()
114        } else {
115            None
116        };
117
118        #[cfg(debug_assertions)]
119        if let Some(vm) = vm.as_ref() {
120            buf.push_str(&format!("<!--hot-reload|{vm}|open-->"));
121        }
122
123        self.inner.to_html_with_buf(
124            buf,
125            position,
126            escape,
127            mark_branches,
128            extra_attrs,
129        );
130
131        #[cfg(debug_assertions)]
132        if let Some(vm) = vm.as_ref() {
133            buf.push_str(&format!("<!--hot-reload|{vm}|close-->"));
134        }
135    }
136
137    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
138        self,
139        buf: &mut StreamBuilder,
140        position: &mut Position,
141        escape: bool,
142        mark_branches: bool,
143        extra_attrs: Vec<AnyAttribute>,
144    ) where
145        Self: Sized,
146    {
147        #[cfg(debug_assertions)]
148        let vm = if option_env!("LEPTOS_WATCH").is_some() {
149            self.view_marker.to_owned()
150        } else {
151            None
152        };
153
154        #[cfg(debug_assertions)]
155        if let Some(vm) = vm.as_ref() {
156            buf.push_sync(&format!("<!--hot-reload|{vm}|open-->"));
157        }
158
159        self.inner.to_html_async_with_buf::<OUT_OF_ORDER>(
160            buf,
161            position,
162            escape,
163            mark_branches,
164            extra_attrs,
165        );
166
167        #[cfg(debug_assertions)]
168        if let Some(vm) = vm.as_ref() {
169            buf.push_sync(&format!("<!--hot-reload|{vm}|close-->"));
170        }
171    }
172
173    fn hydrate<const FROM_SERVER: bool>(
174        self,
175        cursor: &Cursor,
176        position: &PositionState,
177    ) -> Self::State {
178        self.inner.hydrate::<FROM_SERVER>(cursor, position)
179    }
180
181    async fn hydrate_async(
182        self,
183        cursor: &Cursor,
184        position: &PositionState,
185    ) -> Self::State {
186        self.inner.hydrate_async(cursor, position).await
187    }
188
189    fn into_owned(self) -> Self::Owned {
190        View {
191            inner: self.inner.into_owned(),
192            #[cfg(debug_assertions)]
193            view_marker: self.view_marker,
194        }
195    }
196}
197
198impl<T: ToTemplate> ToTemplate for View<T> {
199    fn to_template(
200        buf: &mut String,
201        class: &mut String,
202        style: &mut String,
203        inner_html: &mut String,
204        position: &mut Position,
205    ) {
206        T::to_template(buf, class, style, inner_html, position);
207    }
208}
209
210impl<T: AddAnyAttr> AddAnyAttr for View<T> {
211    type Output<SomeNewAttr: Attribute> = View<T::Output<SomeNewAttr>>;
212
213    fn add_any_attr<NewAttr: Attribute>(
214        self,
215        attr: NewAttr,
216    ) -> Self::Output<NewAttr>
217    where
218        Self::Output<NewAttr>: RenderHtml,
219    {
220        let View {
221            inner,
222            #[cfg(debug_assertions)]
223            view_marker,
224        } = self;
225        View {
226            inner: inner.add_any_attr(attr),
227            #[cfg(debug_assertions)]
228            view_marker,
229        }
230    }
231}
232
233/// Collects some iterator of views into a list, so they can be rendered.
234///
235/// This is a shorthand for `.collect::<Vec<_>>()`, and allows any iterator of renderable
236/// items to be collected into a renderable collection.
237pub trait CollectView {
238    /// The inner view type.
239    type View: IntoView;
240
241    /// Collects the iterator into a list of views.
242    fn collect_view(self) -> Vec<Self::View>;
243}
244
245impl<It, V> CollectView for It
246where
247    It: IntoIterator<Item = V>,
248    V: IntoView,
249{
250    type View = V;
251
252    fn collect_view(self) -> Vec<Self::View> {
253        self.into_iter().collect()
254    }
255}