Skip to main content

trillium_api/
try_from_conn.rs

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