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