heartwood every commit a ring
2.2 KB raw
use axum::{
    extract::{Path, State},
    response::{IntoResponse, Response},
    routing::get,
    Router,
};

use crate::render::{not_found, render};
use crate::AppState;

pub fn router() -> Router<AppState> {
    Router::new().route("/{name}", get(repo_page))
}

async fn repo_page(Path(name): Path<String>, State(state): State<AppState>) -> Response {
    let repo_root = state.config.repo_root.clone();
    let clone_base = state.config.clone_base.clone();
    let name_for_blocking = name.clone();
    let result = tokio::task::spawn_blocking(move || -> anyhow::Result<_> {
        let repo = crate::git::open(&repo_root, &name_for_blocking)?;
        let summary = crate::git::repo_summary(
            &crate::git::resolve_path(&repo_root, &name_for_blocking)?,
            &clone_base,
        )?;
        let head = repo.head_commit().ok();
        let commits = if let Some(h) = head.as_ref() {
            crate::git::recent_commits(&repo, h.id, 10).unwrap_or_default()
        } else {
            Vec::new()
        };
        let readme_html = head.as_ref().and_then(|h| {
            crate::git::read_readme(&repo, h.id).map(|(_, bytes)| {
                let text = String::from_utf8_lossy(&bytes).into_owned();
                let link_base =
                    format!("/{}/blob/{}", name_for_blocking, summary.default_branch);
                let image_base =
                    format!("/{}/raw/{}", name_for_blocking, summary.default_branch);
                crate::markdown::render(&text, &link_base, &image_base)
            })
        });
        Ok((summary, commits, readme_html))
    })
    .await;

    let (summary, commits, readme_html) = match result {
        Ok(Ok(v)) => v,
        Ok(Err(e)) => return not_found(&state, &name, e),
        Err(e) => {
            tracing::error!("repo_page join: {e}");
            return (axum::http::StatusCode::INTERNAL_SERVER_ERROR, "render error")
                .into_response();
        }
    };

    render(
        &state,
        "repo.html",
        &format!("/{name}"),
        minijinja::context! {
            repo => &summary,
            commits => &commits,
            readme_html => readme_html.map(minijinja::Value::from_safe_string),
        },
    )
}