http_request_derive/
from_http_response.rs

1// SPDX-FileCopyrightText: OpenTalk GmbH <mail@opentalk.eu>
2//
3// SPDX-License-Identifier: MIT OR Apache-2.0
4
5use async_trait::async_trait;
6use bytes::Bytes;
7
8use crate::Error;
9
10/// Trait for types that can be converted from `http::Response`
11#[async_trait]
12pub trait FromHttpResponse {
13    /// Convert from `http::Response` to our `Response`
14    ///
15    /// # Errors
16    ///
17    /// The implementation of this trait will map the response to an error if it should be interpreted as such.
18    /// Typical HTTP status code errors are read by the default implementation of [`crate::HttpRequest::read_response`]
19    /// already, so in most cases additional checks are not necessary here.
20    ///
21    /// Of course if the contents of the response cannot be parsed, this will usually be handled as an
22    /// error as well.
23    fn from_http_response(http_response: http::Response<Bytes>) -> Result<Self, Error>
24    where
25        Self: Sized;
26}
27
28#[cfg(feature = "serde")]
29#[async_trait]
30impl<D> FromHttpResponse for D
31where
32    D: serde::de::DeserializeOwned,
33{
34    fn from_http_response(http_response: http::Response<Bytes>) -> Result<Self, Error> {
35        use snafu::ResultExt as _;
36        serde_json::from_slice(http_response.body()).context(crate::error::JsonSnafu)
37    }
38}
39
40#[cfg(all(test, feature = "serde"))]
41mod serde_tests {
42    use http::StatusCode;
43    use serde::Deserialize;
44
45    use super::*;
46
47    #[derive(Deserialize, Debug, PartialEq)]
48    struct TestStruct {
49        id: i32,
50        name: String,
51    }
52
53    #[tokio::test]
54    async fn test_from_response_success() {
55        let response = http::Response::builder()
56            .status(StatusCode::OK)
57            .header("content-type", "application/json")
58            .body(Bytes::from_static(r#"{"id":1,"name":"Test"}"#.as_bytes()))
59            .expect("valid response required");
60
61        let result = TestStruct::from_http_response(response);
62        assert!(result.is_ok());
63        assert_eq!(
64            result.unwrap(),
65            TestStruct {
66                id: 1,
67                name: "Test".to_string()
68            }
69        );
70    }
71
72    #[tokio::test]
73    async fn test_from_response_invalid_json() {
74        let response = http::Response::builder()
75            .status(StatusCode::OK)
76            .header("content-type", "application/json")
77            .body(Bytes::from_static(r#"{"id":1,"name":"Test"#.as_bytes()))
78            .expect("valid response required");
79
80        let result = TestStruct::from_http_response(response);
81        assert!(result.is_err());
82        assert!(matches!(result.unwrap_err(), Error::Json { .. }));
83    }
84}