dioxus_fullstack/
request.rs1use dioxus_fullstack_core::{RequestError, ServerFnError};
2#[cfg(feature = "server")]
3use headers::Header;
4use http::response::Parts;
5use std::{future::Future, pin::Pin};
6
7use crate::{ClientRequest, ClientResponse};
8
9pub trait IntoRequest<R = ClientResponse>: Sized {
29 fn into_request(
30 self,
31 req: ClientRequest,
32 ) -> impl Future<Output = Result<R, RequestError>> + 'static;
33}
34
35impl<A, R> IntoRequest<R> for (A,)
36where
37 A: IntoRequest<R> + 'static + Send,
38{
39 fn into_request(
40 self,
41 req: ClientRequest,
42 ) -> impl Future<Output = Result<R, RequestError>> + 'static {
43 A::into_request(self.0, req)
44 }
45}
46
47pub trait FromResponse<R = ClientResponse>: Sized {
48 fn from_response(res: R) -> impl Future<Output = Result<Self, ServerFnError>>;
49}
50
51impl<A> FromResponse for A
52where
53 A: FromResponseParts,
54{
55 fn from_response(res: ClientResponse) -> impl Future<Output = Result<Self, ServerFnError>> {
56 async move {
57 let (parts, _body) = res.into_parts();
58 let mut parts = parts;
59 A::from_response_parts(&mut parts)
60 }
61 }
62}
63
64impl<A, B> FromResponse for (A, B)
65where
66 A: FromResponseParts,
67 B: FromResponse,
68{
69 fn from_response(res: ClientResponse) -> impl Future<Output = Result<Self, ServerFnError>> {
70 async move {
71 let mut parts = res.make_parts();
72 let a = A::from_response_parts(&mut parts)?;
73 let b = B::from_response(res).await?;
74 Ok((a, b))
75 }
76 }
77}
78
79impl<A, B, C> FromResponse for (A, B, C)
80where
81 A: FromResponseParts,
82 B: FromResponseParts,
83 C: FromResponse,
84{
85 fn from_response(res: ClientResponse) -> impl Future<Output = Result<Self, ServerFnError>> {
86 async move {
87 let mut parts = res.make_parts();
88 let a = A::from_response_parts(&mut parts)?;
89 let b = B::from_response_parts(&mut parts)?;
90 let c = C::from_response(res).await?;
91 Ok((a, b, c))
92 }
93 }
94}
95
96pub trait FromResponseParts
97where
98 Self: Sized,
99{
100 fn from_response_parts(parts: &mut Parts) -> Result<Self, ServerFnError>;
101}
102
103#[cfg(feature = "server")]
104impl<T: Header> FromResponseParts for axum_extra::TypedHeader<T> {
105 fn from_response_parts(parts: &mut Parts) -> Result<Self, ServerFnError> {
106 use headers::HeaderMapExt;
107
108 let t = parts
109 .headers
110 .typed_get::<T>()
111 .ok_or_else(|| ServerFnError::Serialization("Invalid header value".into()))?;
112
113 Ok(axum_extra::TypedHeader(t))
114 }
115}
116
117#[pin_project::pin_project]
121#[must_use = "Requests do nothing unless you `.await` them"]
122pub struct ServerFnRequest<Output> {
123 _phantom: std::marker::PhantomData<Output>,
124 #[pin]
125 fut: Pin<Box<dyn Future<Output = Output> + Send>>,
126}
127
128impl<O> ServerFnRequest<O> {
129 pub fn new(res: impl Future<Output = O> + Send + 'static) -> Self {
130 ServerFnRequest {
131 _phantom: std::marker::PhantomData,
132 fut: Box::pin(res),
133 }
134 }
135}
136
137impl<T, E> std::future::Future for ServerFnRequest<Result<T, E>> {
138 type Output = Result<T, E>;
139
140 fn poll(
141 self: std::pin::Pin<&mut Self>,
142 cx: &mut std::task::Context<'_>,
143 ) -> std::task::Poll<Self::Output> {
144 self.project().fut.poll(cx)
145 }
146}
147
148#[doc(hidden)]
149#[diagnostic::on_unimplemented(
150 message = "The return type of a server function must be `Result<T, E>`",
151 note = "`T` is either `impl IntoResponse` *or* `impl Serialize`",
152 note = "`E` is either `From<ServerFnError> + Serialize`, `dioxus::CapturedError` or `StatusCode`."
153)]
154pub trait AssertIsResult {}
155impl<T, E> AssertIsResult for Result<T, E> {}
156
157#[doc(hidden)]
158pub fn assert_is_result<T: AssertIsResult>() {}
159
160#[diagnostic::on_unimplemented(message = r#"❌ Invalid Arguments to ServerFn ❌
161
162The arguments to the server function must be either:
163
164- a single `impl FromRequest + IntoRequest` argument
165- or multiple `DeserializeOwned` arguments.
166
167Did you forget to implement `IntoRequest` or `Deserialize` for one of the arguments?
168
169`IntoRequest` is a trait that allows payloads to be sent to the server function.
170
171> See https://dioxuslabs.com/learn/0.7/essentials/fullstack/server_functions for more details.
172
173"#)]
174pub trait AssertCanEncode {}
175
176pub struct CantEncode;
177
178pub struct EncodeIsVerified;
179impl AssertCanEncode for EncodeIsVerified {}
180
181#[diagnostic::on_unimplemented(message = r#"❌ Invalid return type from ServerFn ❌
182
183The arguments to the server function must be either:
184
185- a single `impl FromResponse` return type
186- a single `impl Serialize + DeserializedOwned` return type
187
188Did you forget to implement `FromResponse` or `DeserializeOwned` for one of the arguments?
189
190`FromResponse` is a trait that allows payloads to be decoded from the server function response.
191
192> See https://dioxuslabs.com/learn/0.7/essentials/fullstack/server_functions for more details.
193
194"#)]
195pub trait AssertCanDecode {}
196pub struct CantDecode;
197pub struct DecodeIsVerified;
198impl AssertCanDecode for DecodeIsVerified {}
199
200#[doc(hidden)]
201pub fn assert_can_encode(_t: impl AssertCanEncode) {}
202
203#[doc(hidden)]
204pub fn assert_can_decode(_t: impl AssertCanDecode) {}