1use std::sync::Arc;
2
3use anyhow::Context;
4use askama::Template;
5use axum::{extract::Query, response::IntoResponse, Extension};
6use serde::Deserialize;
7
8use crate::{
9 database::schema::{commit::YokedCommit, repository::YokedRepository},
10 into_response,
11 methods::{
12 filters,
13 repo::{Repository, Result, DEFAULT_BRANCHES},
14 },
15};
16
17#[derive(Deserialize)]
18pub struct UriQuery {
19 #[serde(rename = "ofs")]
20 offset: Option<u64>,
21 #[serde(rename = "h")]
22 branch: Option<String>,
23}
24
25#[derive(Template)]
26#[template(path = "repo/log.html")]
27pub struct View {
28 repo: Repository,
29 commits: Vec<YokedCommit>,
30 next_offset: Option<u64>,
31 branch: Option<String>,
32}
33
34pub async fn handle(
35 Extension(repo): Extension<Repository>,
36 Extension(db): Extension<Arc<rocksdb::DB>>,
37 Query(query): Query<UriQuery>,
38) -> Result<impl IntoResponse> {
39 tokio::task::spawn_blocking(move || {
40 let offset = query.offset.unwrap_or(0);
41
42 let repository = crate::database::schema::repository::Repository::open(&db, &*repo)?
43 .context("Repository does not exist")?;
44 let mut commits =
45 get_branch_commits(&repository, &db, query.branch.as_deref(), 101, offset)?;
46
47 let next_offset = if commits.len() == 101 {
48 commits.pop();
49 Some(offset + 100)
50 } else {
51 None
52 };
53
54 Ok(into_response(View {
55 repo,
56 commits,
57 next_offset,
58 branch: query.branch,
59 }))
60 })
61 .await
62 .context("Failed to attach to tokio task")?
63}
64
65pub fn get_branch_commits(
66 repository: &YokedRepository,
67 database: &Arc<rocksdb::DB>,
68 branch: Option<&str>,
69 amount: u64,
70 offset: u64,
71) -> Result<Vec<YokedCommit>> {
72 if let Some(reference) = branch {
73 let commit_tree = repository
74 .get()
75 .commit_tree(database.clone(), &format!("refs/heads/{reference}"));
76 let commit_tree = commit_tree.fetch_latest(amount, offset)?;
77
78 if !commit_tree.is_empty() {
79 return Ok(commit_tree);
80 }
81
82 let tag_tree = repository
83 .get()
84 .commit_tree(database.clone(), &format!("refs/tags/{reference}"));
85 let tag_tree = tag_tree.fetch_latest(amount, offset)?;
86
87 return Ok(tag_tree);
88 }
89
90 for branch in repository
91 .get()
92 .default_branch
93 .as_deref()
94 .into_iter()
95 .chain(DEFAULT_BRANCHES.into_iter())
96 {
97 let commit_tree = repository.get().commit_tree(database.clone(), branch);
98 let commits = commit_tree.fetch_latest(amount, offset)?;
99
100 if !commits.is_empty() {
101 return Ok(commits);
102 }
103 }
104
105 Ok(vec![])
106}