Skip to main content

trillium_api/
try_from_conn.rs

1#[cfg(any(feature = "serde_json", feature = "sonic-rs"))]
2use crate::ApiConnExt;
3use crate::FromConn;
4use std::future::Future;
5use trillium::{BoxedHandler, Conn, Handler};
6
7/// Like FromConn, but with an Error.
8///
9/// If you want to use this directly, Error needs to be Handler.
10///
11/// If Error is not Handler, you can use `Result<T, E> as TryFromConn where T: TryFromConn<Error =
12/// E>`
13///
14/// If extraction is infallible, implement [`FromConn`].
15pub trait TryFromConn: Send + Sync + Sized + 'static {
16    /// The Error type. If this is a Handler, you can extract Self directly in a ApiHandler
17    /// signature, and Error will be called on Conn if try_from_conn fails.
18    type Error: Send + Sync + Sized + 'static;
19
20    /// Attempt to extract Self from &mut Conn, returning Error in case of failure
21    fn try_from_conn(conn: &mut Conn) -> impl Future<Output = Result<Self, Self::Error>> + Send;
22}
23
24#[cfg(feature = "serde_json")]
25#[cfg_attr(docsrs, doc(cfg(feature = "serde_json")))]
26impl TryFromConn for serde_json::Value {
27    type Error = crate::Error;
28
29    async fn try_from_conn(conn: &mut Conn) -> Result<Self, Self::Error> {
30        conn.deserialize().await
31    }
32}
33
34#[cfg(feature = "sonic-rs")]
35#[cfg_attr(docsrs, doc(cfg(feature = "sonic-rs")))]
36impl TryFromConn for sonic_rs::Value {
37    type Error = crate::Error;
38
39    async fn try_from_conn(conn: &mut Conn) -> Result<Self, Self::Error> {
40        conn.deserialize().await
41    }
42}
43
44impl<T: FromConn> TryFromConn for T {
45    type Error = ();
46
47    async fn try_from_conn(conn: &mut Conn) -> Result<Self, Self::Error> {
48        Self::from_conn(conn).await.ok_or(())
49    }
50}
51
52impl TryFromConn for Vec<u8> {
53    type Error = crate::Error;
54
55    async fn try_from_conn(conn: &mut Conn) -> Result<Self, Self::Error> {
56        conn.request_body().read_bytes().await.map_err(Into::into)
57    }
58}
59
60impl TryFromConn for String {
61    type Error = crate::Error;
62
63    async fn try_from_conn(conn: &mut Conn) -> Result<Self, Self::Error> {
64        conn.request_body_string().await.map_err(Into::into)
65    }
66}
67
68#[cfg(feature = "url")]
69#[cfg_attr(docsrs, doc(cfg(feature = "url")))]
70impl TryFromConn for url::Url {
71    type Error = trillium::Status;
72
73    async fn try_from_conn(conn: &mut Conn) -> Result<Self, Self::Error> {
74        let path = conn.path();
75        let host = conn
76            .request_headers()
77            .get_str(trillium::KnownHeaderName::Host)
78            .ok_or(trillium::Status::BadRequest)?;
79        let proto = if conn.is_secure() { "https" } else { "http" };
80        url::Url::parse(&format!("{proto}://{host}{path}"))
81            .map_err(|_| trillium::Status::BadRequest)
82    }
83}
84
85macro_rules! impl_try_from_conn_tuple {
86    ($($name:ident)+) => (
87        impl<$($name),*> TryFromConn for ($($name,)*) where $($name: TryFromConn, <$name as TryFromConn>::Error: Handler),* {
88            type Error = BoxedHandler;
89            #[allow(non_snake_case)]
90            async fn try_from_conn(conn: &mut Conn) -> Result<Self, Self::Error> {
91                $(let $name = <$name as TryFromConn>::try_from_conn(conn)
92                  .await
93                  .map_err(|h| BoxedHandler::new(h))?;)*
94                Ok(($($name, )*))
95            }
96        }
97    )
98}
99
100impl_try_from_conn_tuple! { A B }
101impl_try_from_conn_tuple! { A B C }
102impl_try_from_conn_tuple! { A B C D }
103impl_try_from_conn_tuple! { A B C D E }
104impl_try_from_conn_tuple! { A B C D E F }
105impl_try_from_conn_tuple! { A B C D E F G }
106impl_try_from_conn_tuple! { A B C D E F G H }
107impl_try_from_conn_tuple! { A B C D E F G H I }
108impl_try_from_conn_tuple! { A B C D E F G H I J }
109impl_try_from_conn_tuple! { A B C D E F G H I J K }
110impl_try_from_conn_tuple! { A B C D E F G H I J K L }