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#[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 pub fn new(inner: T) -> Self {
26 Self {
27 inner,
28 #[cfg(debug_assertions)]
29 view_marker: None,
30 }
31 }
32
33 pub fn into_inner(self) -> T {
35 self.inner
36 }
37
38 #[inline(always)]
40 pub fn with_view_marker(
41 #[allow(unused_mut)] mut self,
43 #[allow(unused_variables)] 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
54pub trait IntoView
56where
57 Self: Sized + Render + RenderHtml + Send,
58{
59 fn into_view(self) -> View<Self>;
61}
62
63impl<T> IntoView for T
64where
65 T: Sized + Render + RenderHtml + Send, {
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
225pub trait CollectView {
230 type View: IntoView;
232
233 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}