1use axum::{
2 body::Bytes,
3 extract::{FromRequest, Request},
4 http::StatusCode,
5 response::{IntoResponse, Response},
6};
7use jacquard::{
8 IntoStatic,
9 xrpc::{XrpcEndpoint, XrpcMethod, XrpcRequest},
10};
11use serde_json::json;
12
13pub struct ExtractXrpc<E: XrpcEndpoint>(pub E::Request<'static>);
14
15impl<S, R> FromRequest<S> for ExtractXrpc<R>
16where
17 S: Send + Sync,
18 R: XrpcEndpoint,
19 for<'a> R::Request<'a>: IntoStatic<Output = R::Request<'static>>,
20{
21 type Rejection = Response;
22
23 fn from_request(
24 req: Request,
25 state: &S,
26 ) -> impl Future<Output = Result<Self, Self::Rejection>> + Send {
27 async {
28 match R::METHOD {
29 XrpcMethod::Procedure(_) => {
30 let body = Bytes::from_request(req, state)
31 .await
32 .map_err(IntoResponse::into_response)?;
33 let decoded = R::Request::decode_body(&body);
34 match decoded {
35 Ok(value) => Ok(ExtractXrpc(*value.into_static())),
36 Err(err) => Err((
37 StatusCode::BAD_REQUEST,
38 serde_json::to_string(&json!({
39 "error": "InvalidRequest",
40 "message": format!("failed to decode request: {}", err)
41 }))
42 .expect("Failed to serialize error response"),
43 )
44 .into_response()),
45 }
46 }
47 XrpcMethod::Query => {
48 if let Some(path_query) = req.uri().path_and_query() {
49 let query = path_query.query().unwrap_or("");
50 let value: R::Request<'_> =
51 serde_html_form::from_str::<R::Request<'_>>(query).map_err(|e| {
52 (
53 StatusCode::BAD_REQUEST,
54 serde_json::to_string(&json!({
55 "error": "InvalidRequest",
56 "message": format!("failed to decode request: {}", e)
57 }))
58 .expect("Failed to serialize error response"),
59 )
60 .into_response()
61 })?;
62 Ok(ExtractXrpc(value.into_static()))
63 } else {
64 Err((
65 StatusCode::BAD_REQUEST,
66 serde_json::to_string(&json!({
67 "error": "InvalidRequest",
68 "message": "wrong nsid for wherever this ended up"
69 }))
70 .expect("Failed to serialize error response"),
71 )
72 .into_response())
73 }
74 }
75 }
76 }
77 }
78}
79
80#[derive(Debug, thiserror::Error, miette::Diagnostic)]
81pub enum XrpcRequestError {
82 #[error("Unsupported encoding: {0}")]
83 UnsupportedEncoding(String),
84 #[error("JSON decode error: {0}")]
85 JsonDecodeError(serde_json::Error),
86 #[error("UTF-8 decode error: {0}")]
87 Utf8DecodeError(std::string::FromUtf8Error),
88}