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#[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 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
233pub trait CollectView {
238 type View: IntoView;
240
241 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}