raw
1use std::fs;
2
3use axum::{
4 extract::State,
5 response::{IntoResponse as _, Response},
6};
7
8use crate::{
9 BileState,
10 config::Config,
11 error::Context as _,
12 git::Repository,
13 http::response::{Html, Result},
14 utils::filters,
15};
16
17#[derive(askama::Template)]
18#[template(path = "index.html")]
19struct IndexTemplate<'a> {
20 config: &'a Config,
21 repos: Vec<Repository>,
22}
23
24#[tracing::instrument(skip_all)]
25pub(crate) async fn get(state: State<BileState>) -> Response {
26 state.spawn(move |state| inner(&state)).await
27}
28
29fn inner(state: &BileState) -> Result<Response> {
30 let Ok(read) = fs::read_dir(&state.config.project_root) else {
31 return Ok(Html(IndexTemplate {
32 config: &state.config,
33 repos: Vec::new(),
34 })
35 .into_response());
36 };
37
38 let mut repos = Vec::new();
39
40 for entry in read {
41 let entry = entry.context("failed to open directory entry")?;
42 let metadata = entry.metadata().context("failed to get file metadata")?;
43
44 if !metadata.is_dir() {
45 continue;
46 }
47
48 if entry
49 .file_name()
50 .to_str()
51 .is_some_and(|p| p != "." && p.starts_with('.'))
52 {
53 continue;
54 }
55
56 let Some(repo) = Repository::open_path(&state.config, &entry.path())
57 .context("failed to open repository")?
58 else {
59 continue;
60 };
61
62 if !repo.path().join(&state.config.export_ok).exists() {
65 continue;
66 }
67
68 repos.push(repo);
69 }
70
71 Ok(Html(IndexTemplate {
72 config: &state.config,
73 repos,
74 })
75 .into_response())
76}
77