1use super::{add_attr::AddAnyAttr, Position, PositionState, RenderHtml};
2use crate::{
3 html::attribute::{any_attribute::AnyAttribute, Attribute},
4 hydration::Cursor,
5 ssr::StreamBuilder,
6 view::{iterators::OptionState, Mountable, Render},
7};
8use either_of::Either;
9use std::sync::Arc;
10use throw_error::{Error as AnyError, ErrorHook};
11
12impl<T, E> Render for Result<T, E>
13where
14 T: Render,
15 E: Into<AnyError> + 'static,
16{
17 type State = ResultState<T>;
18
19 fn build(self) -> Self::State {
20 let hook = throw_error::get_error_hook();
21 let (state, error) = match self {
22 Ok(view) => (Either::Left(view.build()), None),
23 Err(e) => (
24 Either::Right(Render::build(())),
25 Some(throw_error::throw(e.into())),
26 ),
27 };
28 ResultState { state, error, hook }
29 }
30
31 fn rebuild(self, state: &mut Self::State) {
32 let _guard = state.hook.clone().map(throw_error::set_error_hook);
33 match (&mut state.state, self) {
34 (Either::Right(_), Err(new)) => {
36 if let Some(old_error) = state.error.take() {
37 throw_error::clear(&old_error);
38 }
39 state.error = Some(throw_error::throw(new.into()));
40 }
41 (Either::Left(old), Ok(new)) => {
43 T::rebuild(new, old);
44 }
45 (Either::Left(old), Err(err)) => {
47 let mut new_state = Render::build(());
48 old.insert_before_this(&mut new_state);
49 old.unmount();
50 state.state = Either::Right(new_state);
51 state.error = Some(throw_error::throw(err));
52 }
53 (Either::Right(old), Ok(new)) => {
55 if let Some(err) = state.error.take() {
56 throw_error::clear(&err);
57 }
58 let mut new_state = new.build();
59 old.insert_before_this(&mut new_state);
60 old.unmount();
61 state.state = Either::Left(new_state);
62 }
63 }
64 }
65}
66
67pub struct ResultState<T>
69where
70 T: Render,
71{
72 state: OptionState<T>,
74 error: Option<throw_error::ErrorId>,
75 hook: Option<Arc<dyn ErrorHook>>,
76}
77
78impl<T> Drop for ResultState<T>
79where
80 T: Render,
81{
82 fn drop(&mut self) {
83 if let Some(e) = self.error.take() {
86 throw_error::clear(&e);
87 }
88 }
89}
90
91impl<T> Mountable for ResultState<T>
92where
93 T: Render,
94{
95 fn unmount(&mut self) {
96 self.state.unmount();
97 }
98
99 fn mount(
100 &mut self,
101 parent: &crate::renderer::types::Element,
102 marker: Option<&crate::renderer::types::Node>,
103 ) {
104 self.state.mount(parent, marker);
105 }
106
107 fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
108 self.state.insert_before_this(child)
109 }
110
111 fn elements(&self) -> Vec<crate::renderer::types::Element> {
112 self.state.elements()
113 }
114}
115
116impl<T, E> AddAnyAttr for Result<T, E>
117where
118 T: AddAnyAttr,
119
120 E: Into<AnyError> + Send + 'static,
121{
122 type Output<SomeNewAttr: Attribute> =
123 Result<<T as AddAnyAttr>::Output<SomeNewAttr>, E>;
124
125 fn add_any_attr<NewAttr: Attribute>(
126 self,
127 attr: NewAttr,
128 ) -> Self::Output<NewAttr>
129 where
130 Self::Output<NewAttr>: RenderHtml,
131 {
132 self.map(|inner| inner.add_any_attr(attr))
133 }
134}
135
136impl<T, E> RenderHtml for Result<T, E>
137where
138 T: RenderHtml,
139 E: Into<AnyError> + Send + 'static,
140{
141 type AsyncOutput = Result<T::AsyncOutput, E>;
142 type Owned = Result<T::Owned, E>;
143
144 const MIN_LENGTH: usize = T::MIN_LENGTH;
145
146 fn dry_resolve(&mut self) {
147 if let Ok(inner) = self.as_mut() {
148 inner.dry_resolve()
149 }
150 }
151
152 async fn resolve(self) -> Self::AsyncOutput {
153 match self {
154 Ok(view) => Ok(view.resolve().await),
155 Err(e) => Err(e),
156 }
157 }
158
159 fn html_len(&self) -> usize {
160 match self {
161 Ok(i) => i.html_len() + 3,
162 Err(_) => 0,
163 }
164 }
165
166 fn to_html_with_buf(
167 self,
168 buf: &mut String,
169 position: &mut super::Position,
170 escape: bool,
171 mark_branches: bool,
172 extra_attrs: Vec<AnyAttribute>,
173 ) {
174 match self {
175 Ok(inner) => {
176 inner.to_html_with_buf(
177 buf,
178 position,
179 escape,
180 mark_branches,
181 extra_attrs,
182 );
183 }
184 Err(e) => {
185 buf.push_str("<!>");
186 throw_error::throw(e);
187 }
188 }
189 }
190
191 fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
192 self,
193 buf: &mut StreamBuilder,
194 position: &mut Position,
195 escape: bool,
196 mark_branches: bool,
197 extra_attrs: Vec<AnyAttribute>,
198 ) where
199 Self: Sized,
200 {
201 match self {
202 Ok(inner) => inner.to_html_async_with_buf::<OUT_OF_ORDER>(
203 buf,
204 position,
205 escape,
206 mark_branches,
207 extra_attrs,
208 ),
209 Err(e) => {
210 buf.push_sync("<!>");
211 throw_error::throw(e);
212 }
213 }
214 }
215
216 fn hydrate<const FROM_SERVER: bool>(
217 self,
218 cursor: &Cursor,
219 position: &PositionState,
220 ) -> Self::State {
221 let hook = throw_error::get_error_hook();
222 let (state, error) = match self {
223 Ok(view) => (
224 Either::Left(view.hydrate::<FROM_SERVER>(cursor, position)),
225 None,
226 ),
227 Err(e) => {
228 let state =
229 RenderHtml::hydrate::<FROM_SERVER>((), cursor, position);
230 (Either::Right(state), Some(throw_error::throw(e.into())))
231 }
232 };
233 ResultState { state, error, hook }
234 }
235
236 async fn hydrate_async(
237 self,
238 cursor: &Cursor,
239 position: &PositionState,
240 ) -> Self::State {
241 let hook = throw_error::get_error_hook();
242 let (state, error) = match self {
243 Ok(view) => (
244 Either::Left(view.hydrate_async(cursor, position).await),
245 None,
246 ),
247 Err(e) => {
248 let state =
249 RenderHtml::hydrate_async((), cursor, position).await;
250 (Either::Right(state), Some(throw_error::throw(e.into())))
251 }
252 };
253 ResultState { state, error, hook }
254 }
255
256 fn into_owned(self) -> Self::Owned {
257 match self {
258 Ok(view) => Ok(view.into_owned()),
259 Err(e) => Err(e),
260 }
261 }
262}