raw
1pub(crate) mod extractor;
2pub(crate) mod path;
3pub(crate) mod response;
4
5use std::sync::Arc;
6
7use axum::response::{IntoResponse as _, Response};
8use http::StatusCode;
9use syntect::parsing::SyntaxSet;
10
11use crate::{config::Config, error::Result, http::response::ErrorPage};
12
13#[derive(Clone)]
14pub(crate) struct BileState {
15 pub(crate) config: Arc<Config>,
16 pub(crate) syntax: Arc<SyntaxSet>,
17}
18
19impl BileState {
20 pub(crate) fn new(config: Config, syntax: SyntaxSet) -> Self {
21 Self {
22 config: Arc::new(config),
23 syntax: Arc::new(syntax),
24 }
25 }
26
27 pub(crate) async fn spawn<F>(&self, f: F) -> Response
28 where
29 F: FnOnce(Self) -> Result<Response> + Send + 'static,
30 {
31 let span = tracing::Span::current();
32
33 let this = self.clone();
34
35 spawn_blocking(move || span.in_scope(|| wrap_err(this.clone(), f(this)))).await
36 }
37}
38
39async fn spawn_blocking<F, R>(f: F) -> R
42where
43 F: FnOnce() -> R + Send + 'static,
44 R: Send + 'static,
45{
46 tokio::task::spawn_blocking(f)
47 .await
48 .expect("failed to join spawn_blocking call, this should only happen due to a panic")
49}
50
51fn wrap_err(state: BileState, res: Result<Response>) -> Response {
52 match res {
53 Ok(res) => res,
54 Err(err) => {
55 tracing::error!(err=?err, "failed to handle response");
56
57 ErrorPage::from(state)
58 .with_status(StatusCode::INTERNAL_SERVER_ERROR)
59 .into_response()
60 }
61 }
62}
63