faucet_cli/serve/
ui_assets.rs1use crate::serve::error::ServeError;
6use axum::extract::Path;
7use axum::http::{HeaderMap, Method, StatusCode, header};
8use axum::response::{IntoResponse, Response};
9
10#[derive(rust_embed::RustEmbed)]
11#[folder = "src/serve/ui/"]
12struct UiAssets;
13
14fn serve_asset(path: &str) -> Response {
17 match UiAssets::get(path) {
18 Some(file) => {
19 let mime = file.metadata.mimetype();
20 (
21 StatusCode::OK,
22 [
23 (header::CONTENT_TYPE, mime.to_string()),
24 (header::CACHE_CONTROL, "no-cache".to_string()),
25 ],
26 file.data.into_owned(),
27 )
28 .into_response()
29 }
30 None => ServeError::NotFound.into_response(),
31 }
32}
33
34pub async fn index() -> Response {
36 serve_asset("index.html")
37}
38
39pub async fn asset(Path(path): Path<String>) -> Response {
41 serve_asset(&path)
42}
43
44pub(crate) fn wants_html(headers: &HeaderMap) -> bool {
47 headers
48 .get(header::ACCEPT)
49 .and_then(|v| v.to_str().ok())
50 .map(|a| a.contains("text/html"))
51 .unwrap_or(false)
52}
53
54pub async fn spa_fallback(method: Method, headers: HeaderMap) -> Response {
58 if method == Method::GET && wants_html(&headers) {
59 index().await
60 } else {
61 ServeError::NotFound.into_response()
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68 use axum::http::HeaderValue;
69
70 #[test]
71 fn wants_html_true_for_browser_accept() {
72 let mut h = HeaderMap::new();
73 h.insert(
74 header::ACCEPT,
75 HeaderValue::from_static("text/html,application/xhtml+xml"),
76 );
77 assert!(wants_html(&h));
78 }
79
80 #[test]
81 fn wants_html_false_for_json_or_missing() {
82 let mut h = HeaderMap::new();
83 h.insert(header::ACCEPT, HeaderValue::from_static("application/json"));
84 assert!(!wants_html(&h));
85 assert!(!wants_html(&HeaderMap::new()));
86 }
87
88 #[test]
89 fn index_asset_is_embedded_and_html() {
90 let resp = serve_asset("index.html");
91 assert_eq!(resp.status(), StatusCode::OK);
92 let ct = resp.headers().get(header::CONTENT_TYPE).unwrap();
93 assert!(ct.to_str().unwrap().contains("text/html"));
94 }
95
96 #[test]
97 fn missing_asset_is_not_found() {
98 let resp = serve_asset("does-not-exist.xyz");
99 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
100 }
101}