raw
1pub mod git;
2
3pub mod error;
4pub mod extractor;
5pub mod filters;
6pub mod markdown;
7pub mod response;
8
9use axum::{
10 http::StatusCode,
11 response::{IntoResponse, Response},
12};
13
14use crate::utils::response::Html;
15
16pub type Result<T = Response, E = Error> = std::result::Result<T, E>;
17
18pub async fn spawn_blocking<F, R>(f: F) -> R
21where
22 F: FnOnce() -> R + Send + 'static,
23 R: Send + 'static,
24{
25 tokio::task::spawn_blocking(f)
26 .await
27 .expect("failed to join spawn_blocking call, this should only happen due to a panic")
28}
29
30#[derive(askama::Template)]
31#[template(path = "error.html")]
32pub enum Error {
33 Failure {
34 status: StatusCode,
35 err: error::Error,
36 },
37 Custom {
38 status: StatusCode,
39 message: String,
40 },
41}
42
43impl Error {
44 pub fn new<M: ToString + ?Sized>(status: StatusCode, message: &M) -> Self {
45 Self::Custom {
46 status,
47 message: message.to_string(),
48 }
49 }
50}
51
52impl IntoResponse for Error {
53 fn into_response(self) -> Response {
54 let status = match &self {
55 Self::Failure { status, err } => {
56 tracing::error!(err=?err, "failed to respond to request");
57
58 status
59 }
60 Self::Custom { status, .. } => status,
61 };
62
63 (*status, Html(self)).into_response()
64 }
65}
66
67impl<E> From<E> for Error
68where
69 E: Into<error::Error>,
70{
71 fn from(err: E) -> Self {
72 Self::Failure {
73 status: StatusCode::INTERNAL_SERVER_ERROR,
74 err: err.into(),
75 }
76 }
77}
78
79#[must_use]
80pub fn blob_mime(blob: &git2::Blob<'_>, extension: &str) -> mime::Mime {
81 extension.parse().unwrap_or_else(|_| {
82 if blob.is_binary() {
83 mime::APPLICATION_OCTET_STREAM
84 } else {
85 mime::TEXT_PLAIN_UTF_8
86 }
87 })
88}
89