eclaw 0.0.12

Personal AI assistant powered by Elph
#![cfg(feature = "web")]

use crate::server::{error::ErrorResponse, web_assets::WebAssets};
use axum::{
    Router,
    body::Body,
    http::{StatusCode, header},
    response::Response,
    routing::get,
};

pub fn router() -> Router {
    Router::new()
        .route("/", get(|| async { serve("index.html").await }))
        .route("/{*path}", get(serve_path))
}

async fn serve_path(axum::extract::Path(path): axum::extract::Path<String>) -> Response {
    if let Some(response) = try_serve(&path) {
        return response;
    }

    serve("index.html").await
}

async fn serve(path: &str) -> Response {
    try_serve(path).unwrap_or_else(|| {
        ErrorResponse::new(
            StatusCode::INTERNAL_SERVER_ERROR,
            format!("embedded web asset `{path}` not found"),
        )
        .into_response(StatusCode::INTERNAL_SERVER_ERROR)
    })
}

fn try_serve(path: &str) -> Option<Response> {
    let file = WebAssets::get(path)?;
    Some(
        Response::builder()
            .status(StatusCode::OK)
            .header(header::CONTENT_TYPE, content_type(path))
            .body(Body::from(file.data))
            .expect("valid embedded response"),
    )
}

fn content_type(path: &str) -> &'static str {
    match path.rsplit('.').next() {
        Some("css") => "text/css; charset=utf-8",
        Some("html") => "text/html; charset=utf-8",
        Some("ico") => "image/x-icon",
        Some("js") | Some("mjs") => "application/javascript",
        Some("json") => "application/json",
        Some("png") => "image/png",
        Some("svg") => "image/svg+xml",
        Some("txt") => "text/plain; charset=utf-8",
        Some("wasm") => "application/wasm",
        Some("woff2") => "font/woff2",
        _ => "application/octet-stream",
    }
}