1use axum::http::header;
2use axum::response::{Html, IntoResponse, Response};
3
4pub fn fonts_list_response(cors_origin: Option<&str>) -> Response {
6 let families = blit_fonts::list_monospace_font_families();
7 let json = format!(
8 "[{}]",
9 families
10 .iter()
11 .map(|f| format!("\"{}\"", f.replace('"', "\\\"")))
12 .collect::<Vec<_>>()
13 .join(",")
14 );
15 let mut resp = (
16 [
17 (header::CONTENT_TYPE, "application/json"),
18 (header::CACHE_CONTROL, "public, max-age=3600"),
19 ],
20 json,
21 )
22 .into_response();
23 add_cors(&mut resp, cors_origin);
24 resp
25}
26
27pub fn font_response(name: &str, cors_origin: Option<&str>) -> Response {
29 match blit_fonts::font_face_css(name) {
30 Some(css) => {
31 let mut resp = (
32 [
33 (header::CONTENT_TYPE, "text/css"),
34 (header::CACHE_CONTROL, "public, max-age=86400, immutable"),
35 ],
36 css,
37 )
38 .into_response();
39 add_cors(&mut resp, cors_origin);
40 resp
41 }
42 None => (axum::http::StatusCode::NOT_FOUND, "font not found").into_response(),
43 }
44}
45
46pub fn font_metrics_response(name: &str, cors_origin: Option<&str>) -> Response {
48 match blit_fonts::font_advance_ratio(name) {
49 Some(ratio) => {
50 let json = format!("{{\"advanceRatio\":{}}}", ratio);
51 let mut resp = (
52 [
53 (header::CONTENT_TYPE, "application/json"),
54 (header::CACHE_CONTROL, "public, max-age=86400, immutable"),
55 ],
56 json,
57 )
58 .into_response();
59 add_cors(&mut resp, cors_origin);
60 resp
61 }
62 None => (axum::http::StatusCode::NOT_FOUND, "font not found").into_response(),
63 }
64}
65
66fn add_cors(resp: &mut Response, origin: Option<&str>) {
67 if let Some(origin) = origin {
68 if let Ok(val) = origin.parse() {
69 resp.headers_mut()
70 .insert(header::ACCESS_CONTROL_ALLOW_ORIGIN, val);
71 }
72 }
73}
74
75pub fn html_response(html: &'static str, etag: &str, if_none_match: Option<&[u8]>) -> Response {
78 if let Some(inm) = if_none_match {
79 if inm == etag.as_bytes() {
80 return (
81 axum::http::StatusCode::NOT_MODIFIED,
82 [(axum::http::header::ETAG, etag)],
83 )
84 .into_response();
85 }
86 }
87 ([(axum::http::header::ETAG, etag)], Html(html)).into_response()
88}
89
90pub fn try_font_route(path: &str, cors_origin: Option<&str>) -> Option<Response> {
94 if path == "/fonts" || path.ends_with("/fonts") {
95 return Some(fonts_list_response(cors_origin));
96 }
97 if let Some(raw) = path.rsplit_once("/font-metrics/").map(|(_, n)| n) {
98 if !raw.contains('/') && !raw.is_empty() {
99 let name = percent_encoding::percent_decode_str(raw).decode_utf8_lossy();
100 return Some(font_metrics_response(&name, cors_origin));
101 }
102 }
103 if let Some(raw) = path.rsplit_once("/font/").map(|(_, n)| n) {
104 if !raw.contains('/') && !raw.is_empty() {
105 let name = percent_encoding::percent_decode_str(raw).decode_utf8_lossy();
106 return Some(font_response(&name, cors_origin));
107 }
108 }
109 None
110}
111
112pub fn html_etag(html: &str) -> String {
114 use std::hash::{Hash, Hasher};
115 let mut h = std::collections::hash_map::DefaultHasher::new();
116 html.hash(&mut h);
117 format!("\"blit-{:x}\"", h.finish())
118}