Skip to main content

gumbo_lib/
view.rs

1use actix_web::HttpResponse;
2use regex::Regex;
3use std::fmt::Display;
4use yew::html::BaseComponent;
5use yew::ServerRenderer;
6
7/// Returns the web facing path to a resource in your application
8/// Will be prefixed with the app_root
9pub 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
18/// Returns the web facing path to a resource in your application
19/// Will be prefixed with the app_root
20/// takes two args useful when creating a path with an Id
21/// output: `/app_root/src/tail`
22pub fn app_path2(src: impl Into<String>, tail: impl Display) -> String {
23    let src = app_path(src);
24    format!("{src}/{tail}")
25}
26
27/// Render a Yew view to send out in an Actix Response
28pub 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    // add the doctype markup. Yew doesn't like to render this.
37    let html = format!("<!DOCTYPE html>\n{html}");
38    Ok(HttpResponse::Ok()
39        .content_type("text/html; charset=utf-8")
40        .body(html))
41}
42
43/// Render a Yew view to send out in an Actix Response
44/// Strips out HTML comments
45pub 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    // add the doctype markup. Yew doesn't like to render this.
55    let html = format!("<!DOCTYPE html>\n{html}");
56    Ok(HttpResponse::Ok()
57        .content_type("text/html; charset=utf-8")
58        .body(html))
59}
60
61/// Render a Yew view to send out in an Actix Response for a Turbo Stream
62pub 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
76/// Render a Yew view to send out in an Actix Response for a Turbo Stream
77/// return the full contents from YEW without stripping comments
78pub 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
91/// Render a Yew view to send out in an Actix Response
92/// Used when a form is not valid
93pub 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
100/// Simple little function to remove HTML comments for the YEW render
101fn strip_html_comments(input: &str) -> String {
102    // (?s) enables "dot matches newline"
103    let re = Regex::new(r"(?s)<!--.*?-->").unwrap();
104    re.replace_all(input, "").into_owned()
105}