raw
1use std::path;
2
3use axum::{
4 extract::State,
5 http::{StatusCode, header},
6 response::{IntoResponse as _, Response},
7};
8
9use crate::{
10 BileState,
11 error::{Context as _, Result},
12 git::Repository,
13 http::{
14 extractor::{ObjectName, Ref, RepoName},
15 path::Path,
16 response::ErrorPage,
17 },
18 utils::blob_mime,
19};
20
21#[tracing::instrument(skip_all)]
22pub(crate) async fn get(
23 state: State<BileState>,
24 Path((repo_name, r#ref, object_name)): Path<(RepoName, Ref, ObjectName)>,
25) -> Response {
26 state
27 .spawn(move |state| inner(&state, &repo_name, &r#ref, &object_name))
28 .await
29}
30
31#[tracing::instrument(skip_all)]
32fn inner(
33 state: &BileState,
34 repo_name: &RepoName,
35 r#ref: &Ref,
36 object_name: &ObjectName,
37) -> Result<Response> {
38 let repo = match Repository::open(&state.config, repo_name).context("opening repository") {
39 Ok(Some(repo)) => repo,
40 Ok(None) => {
41 return Ok(ErrorPage::from(state)
42 .with_status(StatusCode::NOT_FOUND)
43 .into_response());
44 }
45 Err(err) => {
46 tracing::error!(err=?err, "failed to open repository");
47
48 return Ok(ErrorPage::from(state)
49 .with_status(StatusCode::NOT_FOUND)
50 .into_response());
51 }
52 };
53
54 let path = path::Path::new(&object_name.0);
55
56 let Some((_, tree)) = repo
57 .commit_tree(&r#ref.0)
58 .context("failed to get commit tree")?
59 else {
60 return Ok(ErrorPage::from(state)
61 .with_status(StatusCode::NOT_FOUND)
62 .into_response());
63 };
64
65 let Some(blob) = repo.tree_blob(&tree, path)? else {
66 return Ok(ErrorPage::from(state)
67 .with_status(StatusCode::NOT_FOUND)
68 .into_response());
69 };
70
71 let extension = path
72 .extension()
73 .and_then(std::ffi::OsStr::to_str)
74 .unwrap_or_default();
75
76 let mime = blob_mime(&blob, extension);
77
78 Ok((
79 StatusCode::OK,
80 [(header::CONTENT_TYPE, mime.as_ref())],
81 blob.content().to_vec(),
82 )
83 .into_response())
84}
85