1use askama::Template;
2use axum::{extract::Query, response::IntoResponse, Extension};
3use itertools::Itertools;
4use serde::Deserialize;
5use std::path::PathBuf;
6use std::{
7 fmt::{Display, Formatter},
8 sync::Arc,
9};
10
11use crate::{
12 git::{FileWithContent, PathDestination, TreeItem},
13 into_response,
14 methods::{
15 filters,
16 repo::{ChildPath, Repository, RepositoryPath, Result},
17 },
18 Git, ResponseEither,
19};
20
21#[derive(Deserialize)]
22pub struct UriQuery {
23 id: Option<String>,
24 #[serde(default)]
25 raw: bool,
26 #[serde(rename = "h")]
27 branch: Option<Arc<str>>,
28}
29
30impl Display for UriQuery {
31 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
32 let mut prefix = "?";
33
34 if let Some(id) = self.id.as_deref() {
35 write!(f, "{prefix}id={id}")?;
36 prefix = "&";
37 }
38
39 if let Some(branch) = self.branch.as_deref() {
40 write!(f, "{prefix}h={branch}")?;
41 }
42
43 Ok(())
44 }
45}
46
47#[derive(Template)]
48#[template(path = "repo/tree.html")]
49#[allow(clippy::module_name_repetitions)]
50pub struct TreeView {
51 pub repo: Repository,
52 pub items: Vec<TreeItem>,
53 pub query: UriQuery,
54 pub repo_path: PathBuf,
55 pub branch: Option<Arc<str>>,
56}
57
58#[derive(Template)]
59#[template(path = "repo/file.html")]
60pub struct FileView {
61 pub repo: Repository,
62 pub repo_path: PathBuf,
63 pub file: FileWithContent,
64 pub branch: Option<Arc<str>>,
65}
66
67pub async fn handle(
68 Extension(repo): Extension<Repository>,
69 Extension(RepositoryPath(repository_path)): Extension<RepositoryPath>,
70 Extension(ChildPath(child_path)): Extension<ChildPath>,
71 Extension(git): Extension<Arc<Git>>,
72 Query(query): Query<UriQuery>,
73) -> Result<impl IntoResponse> {
74 let open_repo = git.repo(repository_path, query.branch.clone()).await?;
75
76 Ok(
77 match open_repo
78 .path(child_path.clone(), query.id.as_deref(), !query.raw)
79 .await?
80 {
81 PathDestination::Tree(items) => {
82 ResponseEither::Left(ResponseEither::Left(into_response(TreeView {
83 repo,
84 items,
85 branch: query.branch.clone(),
86 query,
87 repo_path: child_path.unwrap_or_default(),
88 })))
89 }
90 PathDestination::File(file) if query.raw => ResponseEither::Right(file.content),
91 PathDestination::File(file) => {
92 ResponseEither::Left(ResponseEither::Right(into_response(FileView {
93 repo,
94 file,
95 branch: query.branch,
96 repo_path: child_path.unwrap_or_default(),
97 })))
98 }
99 },
100 )
101}