1use actix_web::HttpResponse;
2use regex::Regex;
3use std::fmt::Display;
4use yew::html::BaseComponent;
5use yew::ServerRenderer;
6
7pub fn app_path(src: impl Into<String>) -> String {
10 let src: String = src.into();
11 if let Some(tail) = src.strip_prefix("/") {
12 let root = crate::app_root();
13 return format!("{root}{tail}");
14 }
15 src
16}
17
18pub fn app_path2(src: impl Into<String>, tail: impl Display) -> String {
23 let src = app_path(src);
24 format!("{src}/{tail}")
25}
26
27pub async fn render<V, VM, E>(args: VM) -> Result<HttpResponse, E>
29where
30 V: BaseComponent,
31 V: BaseComponent<Properties = VM>,
32 VM: Send + 'static,
33{
34 let renderer = ServerRenderer::<V>::with_props(|| args);
35 let html = renderer.render().await;
36 let html = format!("<!DOCTYPE html>\n{html}");
38 Ok(HttpResponse::Ok()
39 .content_type("text/html; charset=utf-8")
40 .body(html))
41}
42
43pub async fn render_min<V, VM, E>(args: VM) -> Result<HttpResponse, E>
46where
47 V: BaseComponent,
48 V: BaseComponent<Properties = VM>,
49 VM: Send + 'static,
50{
51 let renderer = ServerRenderer::<V>::with_props(|| args);
52 let html = renderer.render().await;
53 let html = strip_html_comments(&html);
54 let html = format!("<!DOCTYPE html>\n{html}");
56 Ok(HttpResponse::Ok()
57 .content_type("text/html; charset=utf-8")
58 .body(html))
59}
60
61pub async fn render_turbo_stream<V, VM, E>(args: VM) -> Result<HttpResponse, E>
63where
64 V: BaseComponent,
65 V: BaseComponent<Properties = VM>,
66 VM: Send + 'static,
67{
68 let renderer = ServerRenderer::<V>::with_props(|| args);
69 let html = renderer.render().await;
70 let html = strip_html_comments(&html);
71 Ok(HttpResponse::Ok()
72 .content_type("text/vnd.turbo-stream.html")
73 .body(html))
74}
75
76pub async fn render_turbo_stream_full<V, VM, E>(args: VM) -> Result<HttpResponse, E>
79where
80 V: BaseComponent,
81 V: BaseComponent<Properties = VM>,
82 VM: Send + 'static,
83{
84 let renderer = ServerRenderer::<V>::with_props(|| args);
85 let html = renderer.render().await;
86 Ok(HttpResponse::Ok()
87 .content_type("text/vnd.turbo-stream.html")
88 .body(html))
89}
90
91pub fn redirect<E>(path: impl Into<String>) -> Result<HttpResponse, E> {
94 let path: String = app_path(path);
95 Ok(HttpResponse::SeeOther()
96 .insert_header(("Location", path))
97 .finish())
98}
99
100fn strip_html_comments(input: &str) -> String {
102 let re = Regex::new(r"(?s)<!--.*?-->").unwrap();
104 re.replace_all(input, "").into_owned()
105}