perseus/template/core/
renderers.rs1use super::utils::PreloadInfo;
2use crate::errors::*;
3#[cfg(engine)]
4use crate::i18n::Translator;
5use crate::path::PathMaybeWithLocale;
6#[cfg(engine)]
7use crate::reactor::Reactor;
8#[cfg(engine)]
9use crate::reactor::RenderMode;
10use crate::state::TemplateState;
11#[cfg(engine)]
12use crate::state::{BuildPaths, StateGeneratorInfo, UnknownStateType};
13#[cfg(engine)]
14use crate::template::default_headers;
15use crate::template::TemplateInner;
16#[cfg(engine)]
17use crate::Request;
18#[cfg(engine)]
19use http::HeaderMap;
20#[cfg(any(client, doc))]
21use sycamore::prelude::ScopeDisposer;
22use sycamore::web::Html;
23#[cfg(engine)]
24use sycamore::web::SsrNode;
25use sycamore::{prelude::Scope, view::View};
26
27impl<G: Html> TemplateInner<G> {
28 #[cfg(any(client, doc))]
33 #[allow(clippy::too_many_arguments)]
34 pub(crate) fn render_for_template_client<'a>(
35 &self,
36 path: PathMaybeWithLocale,
37 state: TemplateState,
38 cx: Scope<'a>,
39 ) -> Result<(View<G>, ScopeDisposer<'a>), ClientError> {
40 assert!(
41 !self.is_capsule,
42 "tried to render capsule with template logic"
43 );
44
45 (self.view)(
47 cx,
48 PreloadInfo {
49 locale: String::new(),
50 was_incremental_match: false,
51 },
52 state,
53 path,
54 )
55 }
56 #[cfg(engine)]
60 pub(crate) fn render_for_template_server(
61 &self,
62 path: PathMaybeWithLocale,
63 state: TemplateState,
64 global_state: TemplateState,
65 mode: RenderMode<SsrNode>,
66 cx: Scope,
67 translator: &Translator,
68 ) -> Result<View<G>, ClientError> {
69 assert!(
70 !self.is_capsule,
71 "tried to render capsule with template logic"
72 );
73
74 Reactor::engine(global_state, mode, Some(translator)).add_self_to_cx(cx);
78 let preload_info = PreloadInfo {};
80 let (view, _) = (self.view)(cx, preload_info, state, path)?;
82 Ok(view)
83 }
84 #[cfg(engine)]
89 pub(crate) fn render_head_str(
90 &self,
91 state: TemplateState,
92 global_state: TemplateState,
93 translator: &Translator,
94 ) -> Result<String, ServerError> {
95 use sycamore::{
96 prelude::create_scope_immediate, utils::hydrate::with_no_hydration_context,
97 };
98
99 let mut prerender_view = Ok(View::empty());
101 create_scope_immediate(|cx| {
102 Reactor::<G>::engine(global_state, RenderMode::Head, Some(translator))
107 .add_self_to_cx(cx);
108
109 prerender_view = with_no_hydration_context(|| {
110 if let Some(head_fn) = &self.head {
111 (head_fn)(cx, state)
112 } else {
113 Ok(View::empty())
114 }
115 });
116 });
117 let prerender_view = prerender_view?;
118 let prerendered = sycamore::render_to_string(|_| prerender_view);
119
120 Ok(prerendered)
121 }
122 #[cfg(engine)]
124 pub(crate) async fn get_build_paths(&self) -> Result<BuildPaths, ServerError> {
125 if let Some(get_build_paths) = &self.get_build_paths {
126 get_build_paths.call().await
127 } else {
128 Err(BuildError::TemplateFeatureNotEnabled {
129 template_name: self.path.clone(),
130 feature_name: "build_paths".to_string(),
131 }
132 .into())
133 }
134 }
135 #[cfg(engine)]
141 pub(crate) async fn get_build_state(
142 &self,
143 info: StateGeneratorInfo<UnknownStateType>,
144 ) -> Result<TemplateState, ServerError> {
145 if let Some(get_build_state) = &self.get_build_state {
146 get_build_state.call(info).await
147 } else {
148 Err(BuildError::TemplateFeatureNotEnabled {
149 template_name: self.path.clone(),
150 feature_name: "build_state".to_string(),
151 }
152 .into())
153 }
154 }
155 #[cfg(engine)]
162 pub(crate) async fn get_request_state(
163 &self,
164 info: StateGeneratorInfo<UnknownStateType>,
165 req: Request,
166 ) -> Result<TemplateState, ServerError> {
167 if let Some(get_request_state) = &self.get_request_state {
168 get_request_state.call(info, req).await
169 } else {
170 Err(BuildError::TemplateFeatureNotEnabled {
171 template_name: self.path.clone(),
172 feature_name: "request_state".to_string(),
173 }
174 .into())
175 }
176 }
177 #[cfg(engine)]
185 pub(crate) async fn amalgamate_states(
186 &self,
187 info: StateGeneratorInfo<UnknownStateType>,
188 build_state: TemplateState,
189 request_state: TemplateState,
190 ) -> Result<TemplateState, ServerError> {
191 if let Some(amalgamate_states) = &self.amalgamate_states {
192 amalgamate_states
193 .call(info, build_state, request_state)
194 .await
195 } else {
196 Err(BuildError::TemplateFeatureNotEnabled {
197 template_name: self.path.clone(),
198 feature_name: "amalgamate_states".to_string(),
199 }
200 .into())
201 }
202 }
203 #[cfg(engine)]
209 pub(crate) async fn should_revalidate(
210 &self,
211 info: StateGeneratorInfo<UnknownStateType>,
212 req: Request,
213 ) -> Result<bool, ServerError> {
214 if let Some(should_revalidate) = &self.should_revalidate {
215 should_revalidate.call(info, req).await
216 } else {
217 Err(BuildError::TemplateFeatureNotEnabled {
218 template_name: self.path.clone(),
219 feature_name: "should_revalidate".to_string(),
220 }
221 .into())
222 }
223 }
224 #[cfg(engine)]
234 pub(crate) fn get_headers(
235 &self,
236 state: TemplateState,
237 global_state: TemplateState,
238 translator: Option<&Translator>,
239 ) -> Result<HeaderMap, ServerError> {
240 use sycamore::prelude::create_scope_immediate;
241
242 let mut res = Ok(HeaderMap::new());
243 create_scope_immediate(|cx| {
244 let reactor = Reactor::<G>::engine(global_state, RenderMode::Headers, translator);
245 reactor.add_self_to_cx(cx);
246
247 if let Some(header_fn) = &self.set_headers {
248 res = (header_fn)(cx, state);
249 } else {
250 res = Ok(default_headers());
251 }
252 });
253
254 res
255 }
256}