a73x

src/server/http/repo/commits.rs

Ref:   Size: 3.1 KiB

use std::sync::Arc;

use axum::extract::{Path, State};
use axum::response::{IntoResponse, Response};

use super::{AppState, OverviewCommit, collab_counts, head_branch_name, list_branches, open_repo};

#[derive(askama::Template, askama_web::WebTemplate)]
#[template(path = "commits.html")]
pub struct CommitsTemplate {
    pub site_title: String,
    pub repo_name: String,
    pub active_section: String,
    pub open_patches: usize,
    pub open_issues: usize,
    pub ref_name: String,
    pub branches: Vec<String>,
    pub commits: Vec<OverviewCommit>,
}

fn commits_for_ref(repo: &git2::Repository, ref_name: &str, limit: usize) -> Vec<OverviewCommit> {
    let obj = match repo.revparse_single(ref_name) {
        Ok(o) => o,
        Err(_) => return Vec::new(),
    };
    let commit = match obj.peel_to_commit() {
        Ok(c) => c,
        Err(_) => return Vec::new(),
    };
    let mut revwalk = match repo.revwalk() {
        Ok(rw) => rw,
        Err(_) => return Vec::new(),
    };
    if revwalk.push(commit.id()).is_err() {
        return Vec::new();
    }
    revwalk
        .take(limit)
        .filter_map(|oid| {
            let oid = oid.ok()?;
            let commit = repo.find_commit(oid).ok()?;
            let id = oid.to_string();
            let short_id = id[..8.min(id.len())].to_string();
            let summary = commit.summary().unwrap_or("").to_string();
            let author = commit.author().name().unwrap_or("").to_string();
            let secs = commit.time().seconds();
            let date = chrono::TimeZone::timestamp_opt(&chrono::Utc, secs, 0)
                .single()
                .map(|dt| dt.format("%Y-%m-%d").to_string())
                .unwrap_or_default();
            Some(OverviewCommit { id, short_id, summary, author, date })
        })
        .collect()
}

pub async fn commits(
    Path(repo_name): Path<String>,
    State(state): State<Arc<AppState>>,
) -> Response {
    let (_entry, repo) = match open_repo(&state, &repo_name) {
        Ok(r) => r,
        Err(resp) => return resp,
    };

    let ref_name = head_branch_name(&repo);
    let branches = list_branches(&repo);
    let commits = commits_for_ref(&repo, &ref_name, 200);
    let (open_patches, open_issues) = collab_counts(&repo);

    CommitsTemplate {
        site_title: state.site_title.clone(),
        repo_name,
        active_section: "commits".to_string(),
        open_patches,
        open_issues,
        ref_name,
        branches,
        commits,
    }
    .into_response()
}

pub async fn commits_ref(
    Path((repo_name, ref_name)): Path<(String, String)>,
    State(state): State<Arc<AppState>>,
) -> Response {
    let (_entry, repo) = match open_repo(&state, &repo_name) {
        Ok(r) => r,
        Err(resp) => return resp,
    };

    let branches = list_branches(&repo);
    let commits = commits_for_ref(&repo, &ref_name, 200);
    let (open_patches, open_issues) = collab_counts(&repo);

    CommitsTemplate {
        site_title: state.site_title.clone(),
        repo_name,
        active_section: "commits".to_string(),
        open_patches,
        open_issues,
        ref_name,
        branches,
        commits,
    }
    .into_response()
}