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
64pub(crate) struct PermissionsPolicy;
65
66impl tower_helmet::IntoHeader for PermissionsPolicy {
67 fn header_name(&self) -> http::HeaderName {
68 http::HeaderName::from_static("permissions-policy")
69 }
70
71 fn header_value(
72 &self,
73 ) -> std::result::Result<http::HeaderValue, http::header::InvalidHeaderValue> {
74 http::HeaderValue::from_str(
75 "accelerometer=(),ambient-light-sensor=(),attribution-reporting=(),autoplay=(),bluetooth=(),browsing-topics=(),camera=(),captured-surface-control=(),clipboard-read=(),clipboard-write=(),compute-pressure=(),display-capture=(),document-domain=(),encrypted-media=(),fullscreen=(),gamepad=(),geolocation=(),gyroscope=(),hid=(),identity-credentials-get=(),idle-detection=(),interest-cohort=(),local-fonts=(),magnetometer=(),microphone=(),midi=(),on-device-speech-recognition=(),otp-credentials=(),payment=(),picture-in-picture=(),private-state-token-issuance=(),private-state-token-redemption=(),publickey-credentials-create=(),publickey-credentials-get=(),screen-wake-lock=(),serial=(),speaker-selection=(),storage-access=(),sync-xhr=(),usb=(),web-share=(),window-management=(),xr-spatial-tracking=()",
76 )
77 }
78}
79