oxihttp_core/
response_ext.rs1use bytes::{Buf, Bytes};
7use http::Response;
8use http_body_util::BodyExt as _;
9
10use crate::error::OxiHttpError;
11
12#[allow(async_fn_in_trait)]
27pub trait ResponseExt: Sized {
28 async fn body_bytes(self) -> Result<Bytes, OxiHttpError>;
30
31 async fn body_text(self) -> Result<String, OxiHttpError>;
33
34 async fn body_json<T: serde::de::DeserializeOwned>(self) -> Result<T, OxiHttpError>;
36}
37
38impl<B> ResponseExt for Response<B>
39where
40 B: http_body::Body + Send,
41 B::Data: Buf,
42 B::Error: std::fmt::Display,
43{
44 async fn body_bytes(self) -> Result<Bytes, OxiHttpError> {
45 let body = self.into_body();
46 let collected = body
47 .collect()
48 .await
49 .map_err(|e| OxiHttpError::Body(e.to_string()))?;
50 Ok(collected.to_bytes())
51 }
52
53 async fn body_text(self) -> Result<String, OxiHttpError> {
54 let bytes = self.body_bytes().await?;
55 String::from_utf8(bytes.to_vec())
56 .map_err(|e| OxiHttpError::Body(format!("invalid UTF-8: {e}")))
57 }
58
59 async fn body_json<T: serde::de::DeserializeOwned>(self) -> Result<T, OxiHttpError> {
60 let bytes = self.body_bytes().await?;
61 serde_json::from_slice(&bytes).map_err(|e| OxiHttpError::Json(e.to_string()))
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68 use bytes::Bytes;
69 use http::Response;
70 use http_body_util::Full;
71
72 #[tokio::test]
73 async fn test_body_bytes() {
74 let resp: Response<Full<Bytes>> = Response::new(Full::new(Bytes::from("hello")));
75 let bytes = resp.body_bytes().await.expect("collect succeeds");
76 assert_eq!(bytes.as_ref(), b"hello");
77 }
78
79 #[tokio::test]
80 async fn test_body_text() {
81 let resp: Response<Full<Bytes>> = Response::new(Full::new(Bytes::from("hello text")));
82 let text = resp.body_text().await.expect("decode succeeds");
83 assert_eq!(text, "hello text");
84 }
85
86 #[tokio::test]
87 async fn test_body_json() {
88 #[derive(serde::Deserialize, PartialEq, Debug)]
89 struct Msg {
90 value: u32,
91 }
92
93 let json = br#"{"value":42}"#;
94 let resp: Response<Full<Bytes>> = Response::new(Full::new(Bytes::from(json.as_ref())));
95 let msg: Msg = resp.body_json().await.expect("deserialise succeeds");
96 assert_eq!(msg, Msg { value: 42 });
97 }
98
99 #[tokio::test]
100 async fn test_body_text_invalid_utf8() {
101 let resp: Response<Full<Bytes>> = Response::new(Full::new(Bytes::from(vec![0xFF, 0xFE])));
102 let result = resp.body_text().await;
103 assert!(result.is_err());
104 let err_msg = result.unwrap_err().to_string();
105 assert!(err_msg.contains("invalid UTF-8"), "got: {err_msg}");
106 }
107
108 #[tokio::test]
109 async fn test_body_json_invalid() {
110 let resp: Response<Full<Bytes>> = Response::new(Full::new(Bytes::from("not json")));
111 let result = resp.body_json::<serde_json::Value>().await;
112 assert!(result.is_err());
113 }
114}