stellation_backend/
renderer.rs1use std::fmt;
2use std::fmt::Write;
3use std::marker::PhantomData;
4use std::rc::Rc;
5
6use bounce::helmet::render_static;
7use stellation_bridge::links::{Link, PhantomLink};
8use stellation_bridge::Bridge;
9use yew::BaseComponent;
10
11use crate::hooks::HeadContents;
12use crate::request::RenderRequest;
13use crate::root::{StellationRoot, StellationRootProps};
14use crate::{html, ServerAppProps};
15
16pub struct ServerRenderer<COMP, REQ = (), CTX = (), L = PhantomLink>
29where
30 COMP: BaseComponent,
31{
32 request: REQ,
33 bridge: Option<Bridge<L>>,
34 _marker: PhantomData<(COMP, REQ, CTX)>,
35}
36
37impl<COMP, REQ, CTX, L> fmt::Debug for ServerRenderer<COMP, REQ, CTX, L>
38where
39 COMP: BaseComponent,
40{
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 f.write_str("ServerRenderer<_>")
43 }
44}
45
46impl<COMP, REQ, CTX> ServerRenderer<COMP, REQ, CTX>
47where
48 COMP: BaseComponent<Properties = ServerAppProps<CTX, REQ>>,
49{
50 pub fn new(request: REQ) -> ServerRenderer<COMP, REQ, CTX> {
52 ServerRenderer {
53 request,
54 bridge: None,
55 _marker: PhantomData,
56 }
57 }
58}
59
60impl<COMP, REQ, CTX, L> ServerRenderer<COMP, REQ, CTX, L>
61where
62 COMP: BaseComponent<Properties = ServerAppProps<CTX, REQ>>,
63{
64 pub fn bridge<T>(self, bridge: Bridge<T>) -> ServerRenderer<COMP, REQ, CTX, T> {
66 ServerRenderer {
67 request: self.request,
68 bridge: Some(bridge),
69 _marker: PhantomData,
70 }
71 }
72
73 pub async fn render(self) -> String
79 where
80 CTX: 'static,
81 REQ: 'static,
82 L: 'static + Link,
83 REQ: RenderRequest<Context = CTX>,
84 {
85 let Self {
86 bridge, request, ..
87 } = self;
88
89 let mut head_s = String::new();
90 let mut helmet_tags = Vec::new();
91 let mut body_s = String::new();
92
93 let request: Rc<_> = request.into();
94
95 if !request.is_client_only() {
96 let head_contents = HeadContents::new();
97
98 let (reader, writer) = render_static();
99
100 let props = ServerAppProps::from_request(request.clone());
101
102 body_s = yew::LocalServerRenderer::<StellationRoot<COMP, CTX, REQ, L>>::with_props(
103 StellationRootProps {
104 server_app_props: props,
105 helmet_writer: writer,
106 bridge,
107 head_contents: head_contents.clone(),
108 },
109 )
110 .render()
111 .await;
112
113 helmet_tags.append(&mut reader.render().await);
114 let _ = write!(
115 &mut head_s,
116 r#"<meta name="stellation-mode" content="hydrate">"#
117 );
118
119 head_contents.render_into(&mut head_s).await;
120 }
121
122 html::format_html(request.template(), helmet_tags, head_s, body_s).await
123 }
124}