raw
1use std::path;
2
3use axum::{
4 extract::Path,
5 http::{StatusCode, header},
6 response::{IntoResponse as _, Response},
7};
8
9use crate::utils::{
10 Error, Result, blob_mime, error::Context as _, extractor::repo_name_checks, git::Repository,
11 spawn_blocking,
12};
13
14#[tracing::instrument(skip_all)]
15pub async fn get(
16 Path((repo_name, r#ref, object_name)): Path<(String, String, String)>,
17) -> Response {
18 spawn_blocking(move || inner(&repo_name, &r#ref, &object_name).into_response()).await
19}
20
21#[tracing::instrument(skip_all)]
22fn inner(repo_name: &str, r#ref: &str, object_name: &str) -> Result {
23 repo_name_checks(repo_name)?;
24
25 let Some(repo) = Repository::open(repo_name).context("opening repository")? else {
26 return Err(Error::new(StatusCode::NOT_FOUND, "repo does not exist"));
27 };
28
29 let path = path::Path::new(&object_name);
30
31 let Some((_, tree)) = repo
32 .commit_tree(r#ref)
33 .context("failed to get commit tree")?
34 else {
35 return Err(Error::new(
36 StatusCode::NOT_FOUND,
37 "commit does not exist in repo",
38 ));
39 };
40
41 let Some(blob) = repo.tree_blob(&tree, path)? else {
42 return Err(Error::new(
43 StatusCode::NOT_FOUND,
44 "file does not exist into repo",
45 ));
46 };
47
48 let extension = path
49 .extension()
50 .and_then(std::ffi::OsStr::to_str)
51 .unwrap_or_default();
52
53 let mime = blob_mime(&blob, extension);
54
55 Ok((
56 StatusCode::OK,
57 [(header::CONTENT_TYPE, mime.as_ref())],
58 blob.content().to_vec(),
59 )
60 .into_response())
61}
62