1use std::sync::Arc;
10
11use axum::Router;
12use axum::extract::State;
13use axum::http::{StatusCode, header};
14use axum::response::{IntoResponse, Response};
15use axum::routing::get;
16
17use crate::assets::{AssetReply, ClientAssets};
18
19use super::config::refusal_text;
20
21pub fn bundle_routes<S>(client: Arc<ClientAssets>) -> Router<S>
31where
32 S: Clone + Send + Sync + 'static,
33{
34 Router::new()
35 .route("/", get(serve_index))
36 .route("/{*asset}", get(serve_asset))
37 .with_state(client)
38}
39
40async fn serve_index(State(client): State<Arc<ClientAssets>>) -> Response {
41 asset_response(client.lookup("index.html"))
42}
43
44async fn serve_asset(
45 State(client): State<Arc<ClientAssets>>,
46 axum::extract::Path(asset): axum::extract::Path<String>,
47) -> Response {
48 asset_response(client.lookup(&asset))
49}
50
51fn asset_response(reply: AssetReply) -> Response {
53 match reply {
54 AssetReply::Found {
55 bytes,
56 content_type,
57 gzipped: true,
58 } => (
59 StatusCode::OK,
60 [
61 (header::CONTENT_TYPE, content_type),
62 (header::CONTENT_ENCODING, "gzip"),
63 ],
64 bytes,
65 )
66 .into_response(),
67 AssetReply::Found {
68 bytes,
69 content_type,
70 gzipped: false,
71 } => (
72 StatusCode::OK,
73 [(header::CONTENT_TYPE, content_type)],
74 bytes,
75 )
76 .into_response(),
77 AssetReply::Miss(err) => (StatusCode::NOT_FOUND, refusal_text(&err)).into_response(),
78 }
79}