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
94    async fn resolve(self) -> Self::AsyncOutput {
95        self.inner.resolve().await
96    }
97
98    fn dry_resolve(&mut self) {
99        self.inner.dry_resolve();
100    }
101
102    fn to_html_with_buf(
103        self,
104        buf: &mut String,
105        position: &mut Position,
106        escape: bool,
107        mark_branches: bool,
108        extra_attrs: Vec<AnyAttribute>,
109    ) {
110        #[cfg(all(debug_assertions, feature = "nightly", rustc_nightly))]
111        let vm = self.view_marker.to_owned();
112        #[cfg(all(debug_assertions, feature = "nightly", rustc_nightly))]
113        if let Some(vm) = vm.as_ref() {
114            buf.push_str(&format!("<!--hot-reload|{vm}|open-->"));
115        }
116
117        self.inner.to_html_with_buf(
118            buf,
119            position,
120            escape,
121            mark_branches,
122            extra_attrs,
123        );
124
125        #[cfg(all(debug_assertions, feature = "nightly", rustc_nightly))]
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        escape: bool,
136        mark_branches: bool,
137        extra_attrs: Vec<AnyAttribute>,
138    ) where
139        Self: Sized,
140    {
141        #[cfg(all(debug_assertions, feature = "nightly", rustc_nightly))]
142        let vm = self.view_marker.to_owned();
143        #[cfg(all(debug_assertions, feature = "nightly", rustc_nightly))]
144        if let Some(vm) = vm.as_ref() {
145            buf.push_sync(&format!("<!--hot-reload|{vm}|open-->"));
146        }
147
148        self.inner.to_html_async_with_buf::<OUT_OF_ORDER>(
149            buf,
150            position,
151            escape,
152            mark_branches,
153            extra_attrs,
154        );
155
156        #[cfg(all(debug_assertions, feature = "nightly", rustc_nightly))]
157        if let Some(vm) = vm.as_ref() {
158            buf.push_sync(&format!("<!--hot-reload|{vm}|close-->"));
159        }
160    }
161
162    fn hydrate<const FROM_SERVER: bool>(
163        self,
164        cursor: &Cursor,
165        position: &PositionState,
166    ) -> Self::State {
167        self.inner.hydrate::<FROM_SERVER>(cursor, position)
168    }
169
170    fn into_owned(self) -> Self::Owned {
171        View {
172            inner: self.inner.into_owned(),
173            #[cfg(debug_assertions)]
174            view_marker: self.view_marker,
175        }
176    }
177}
178
179impl<T: ToTemplate> ToTemplate for View<T> {
180    fn to_template(
181        buf: &mut String,
182        class: &mut String,
183        style: &mut String,
184        inner_html: &mut String,
185        position: &mut Position,
186    ) {
187        T::to_template(buf, class, style, inner_html, position);
188    }
189}
190
191impl<T: AddAnyAttr> AddAnyAttr for View<T> {
192    type Output<SomeNewAttr: Attribute> = View<T::Output<SomeNewAttr>>;
193
194    fn add_any_attr<NewAttr: Attribute>(
195        self,
196        attr: NewAttr,
197    ) -> Self::Output<NewAttr>
198    where
199        Self::Output<NewAttr>: RenderHtml,
200    {
201        let View {
202            inner,
203            #[cfg(debug_assertions)]
204            view_marker,
205        } = self;
206        View {
207            inner: inner.add_any_attr(attr),
208            #[cfg(debug_assertions)]
209            view_marker,
210        }
211    }
212}
213
214/// Collects some iterator of views into a list, so they can be rendered.
215///
216/// This is a shorthand for `.collect::<Vec<_>>()`, and allows any iterator of renderable
217/// items to be collected into a renderable collection.
218pub trait CollectView {
219    /// The inner view type.
220    type View: IntoView;
221
222    /// Collects the iterator into a list of views.
223    fn collect_view(self) -> Vec<Self::View>;
224}
225
226impl<It, V> CollectView for It
227where
228    It: IntoIterator<Item = V>,
229    V: IntoView,
230{
231    type View = V;
232
233    fn collect_view(self) -> Vec<Self::View> {
234        self.into_iter().collect()
235    }
236}