use crate::service::web::{
extract::{FromRequest, OptionalFromRequest},
response::IntoResponse,
};
use rama_core::telemetry::tracing;
use rama_http_types::{BodyExtractExt, Method, Request, Response, StatusCode};
use serde::{Deserialize, de::DeserializeOwned};
#[derive(Debug)]
pub struct ReadSignals<T>(pub T);
#[derive(Deserialize)]
struct DatastarParam {
datastar: serde_json::Value,
}
impl<T> FromRequest for ReadSignals<T>
where
T: DeserializeOwned + Send + Sync + 'static,
{
type Rejection = Response;
async fn from_request(req: Request) -> Result<Self, Self::Rejection> {
let json = match *req.method() {
Method::GET => {
let param = req.uri().query_params::<DatastarParam>().map_err(|err| {
tracing::debug!(
"failed to parse datastar query params from GET request: {err:?}"
);
(StatusCode::BAD_REQUEST, err.to_string()).into_response()
})?;
let signals = param.datastar.as_str().ok_or_else(|| {
tracing::debug!("failed to get datastar query value from GET request");
(StatusCode::BAD_REQUEST, "Failed to parse JSON").into_response()
})?;
serde_json::from_str(signals).map_err(|err| {
tracing::debug!(
"failed to parse datastar query json value from GET request: {err:?}"
);
(StatusCode::BAD_REQUEST, err.to_string()).into_response()
})?
}
_ => req.into_body().try_into_json().await.map_err(|err| {
tracing::debug!("failed to parse datastar json payload from POST request: {err:?}");
(StatusCode::BAD_REQUEST, err.to_string()).into_response()
})?,
};
Ok(Self(json))
}
}
impl<T> OptionalFromRequest for ReadSignals<T>
where
T: DeserializeOwned + Send + Sync + 'static,
{
type Rejection = Response;
async fn from_request(req: Request) -> Result<Option<Self>, Self::Rejection> {
if req.headers().get("datastar-request").is_none() {
tracing::trace!(
"no datastar request header present: returning no read signals as such"
);
return Ok(None);
}
Ok(Some(<Self as FromRequest>::from_request(req).await?))
}
}