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