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