dioxus_fullstack/payloads/
axum_types.rs

1use super::*;
2use crate::{ClientResponse, FromResponse};
3pub use axum::extract::Json;
4use axum::response::{Html, NoContent, Redirect};
5use dioxus_fullstack_core::{RequestError, ServerFnError};
6use futures::StreamExt;
7use http::StatusCode;
8use std::future::Future;
9
10impl<T: From<String>> FromResponse for Html<T> {
11    fn from_response(res: ClientResponse) -> impl Future<Output = Result<Self, ServerFnError>> {
12        async move {
13            let content = res.text().await?;
14            Ok(Html(content.into()))
15        }
16    }
17}
18
19impl<T> IntoRequest for Json<T>
20where
21    T: Serialize + 'static + DeserializeOwned,
22{
23    fn into_request(self, request: ClientRequest) -> impl Future<Output = ClientResult> + 'static {
24        async move { request.send_json(&self.0).await }
25    }
26}
27
28impl<T: DeserializeOwned> FromResponse for Json<T> {
29    fn from_response(res: ClientResponse) -> impl Future<Output = Result<Self, ServerFnError>> {
30        async move {
31            let data = res.json::<T>().await?;
32            Ok(Json(data))
33        }
34    }
35}
36
37impl FromResponse for Redirect {
38    fn from_response(res: ClientResponse) -> impl Future<Output = Result<Self, ServerFnError>> {
39        async move {
40            let location = res
41                .headers()
42                .get(http::header::LOCATION)
43                .ok_or_else(|| RequestError::Redirect("Missing Location header".into()))?
44                .to_str()
45                .map_err(|_| RequestError::Redirect("Invalid Location header".into()))?;
46            match res.status() {
47                StatusCode::SEE_OTHER => Ok(Redirect::to(location)),
48                StatusCode::TEMPORARY_REDIRECT => Ok(Redirect::temporary(location)),
49                StatusCode::PERMANENT_REDIRECT => Ok(Redirect::permanent(location)),
50                _ => Err(RequestError::Redirect("Not a redirect status code".into()).into()),
51            }
52        }
53    }
54}
55
56impl FromResponse for NoContent {
57    fn from_response(res: ClientResponse) -> impl Future<Output = Result<Self, ServerFnError>> {
58        async move {
59            let status = res.status();
60            if status == StatusCode::NO_CONTENT {
61                Ok(NoContent)
62            } else {
63                let body = res.text().await.unwrap_or_else(|_| "".into());
64                Err(RequestError::Status(body, status.into()).into())
65            }
66        }
67    }
68}
69
70/// Implementation of `FromResponse` for `axum::response::Response`.
71///
72/// This allows converting a `ClientResponse` (from a client-side HTTP request)
73/// into an `axum::Response` for server-side handling. The response's status,
74/// headers, and body are transferred from the client response to the axum response.
75impl FromResponse for axum::response::Response {
76    fn from_response(res: ClientResponse) -> impl Future<Output = Result<Self, ServerFnError>> {
77        async move {
78            let parts = res.make_parts();
79            let body = axum::body::Body::from_stream(res.bytes_stream());
80            let response = axum::response::Response::from_parts(parts, body);
81            Ok(response)
82        }
83    }
84}
85
86/// Implementation of `IntoRequest` for `axum::extract::Request`.
87///
88/// This allows converting an `axum::Request` (from server-side extraction)
89/// into a `ClientRequest` that can be sent as an HTTP request. The request's
90/// headers and body are transferred from the axum request to the client request.
91impl IntoRequest for axum::extract::Request {
92    fn into_request(
93        self,
94        mut request: ClientRequest,
95    ) -> impl Future<Output = Result<ClientResponse, RequestError>> + 'static {
96        async move {
97            let (parts, body) = self.into_parts();
98
99            for (key, value) in &parts.headers {
100                request = request.header(key, value)?;
101            }
102
103            request
104                .send_body_stream(
105                    body.into_data_stream()
106                        .map(|res| res.map_err(|_| StreamingError::Failed)),
107                )
108                .await
109        }
110    }
111}