Parent [>]
1//! Route handlers for branch pages.
2//!
3//! Covers two routes: the branch list (`/{repo}/branches`) and the branch
4//! detail page (`/{repo}/branches/{*branch}`).  The detail handler resolves
5//! the branch ref to its tip commit and walks ancestors to produce a log view.
6
7use askama::Template;
8use askama_web::WebTemplate;
9use axum::extract::{Path, Query, State};
10use axum::response::IntoResponse;
11use serde::Deserialize;
12
13use crate::error::Result;
14use crate::git;
15use crate::routes::{AppState, RepoName};
16
17const PAGE_SIZE: usize = 100;
18
19#[derive(Deserialize)]
20pub(super) struct BranchPath {
21    branch: String,
22}
23
24#[derive(Deserialize)]
25pub(super) struct Pagination {
26    #[serde(default)]
27    after: Option<String>,
28}
29
30/// Template context for the branch list page.
31#[derive(Template, WebTemplate)]
32#[template(path = "branch_list.html")]
33struct BranchList {
34    repo: String,
35    branches: Vec<git::BranchInfo>,
36}
37
38/// Template context for the branch detail (log) page.
39#[derive(Template, WebTemplate)]
40#[template(path = "branch_detail.html")]
41struct BranchDetail {
42    repo: String,
43    branch: String,
44    tip_sha: String,
45    commits: Vec<git::CommitSummary>,
46    after: Option<String>,
47    next_cursor: Option<String>,
48}
49
50/// Renders the full list of branches for a repository.
51pub async fn list(
52    State(state): State<AppState>,
53    RepoName(repo): RepoName,
54) -> Result<impl IntoResponse> {
55    let git_repo = git::open_repo(&state.root, &repo)?;
56    let branches = git::load_branches(&git_repo);
57    Ok(BranchList { repo, branches })
58}
59
60/// Renders a paginated log of commits reachable from the tip of the given branch.
61pub async fn detail(
62    State(state): State<AppState>,
63    RepoName(repo): RepoName,
64    Path(params): Path<BranchPath>,
65    Query(pagination): Query<Pagination>,
66) -> Result<impl IntoResponse> {
67    let after = pagination.after.filter(|s| !s.is_empty());
68    let git_repo = git::open_repo(&state.root, &repo)?;
69    let log = git::load_branch_log(&git_repo, &params.branch, after.clone(), PAGE_SIZE)?;
70
71    Ok(BranchDetail {
72        repo,
73        branch: params.branch,
74        tip_sha: log.tip_sha,
75        commits: log.commits,
76        after,
77        next_cursor: log.next_cursor,
78    })
79}