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
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
214pub trait CollectView {
219 type View: IntoView;
221
222 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}