typespec/http/
response.rs1use crate::{
7 error::{ErrorKind, ResultExt as _},
8 http::{headers::Headers, StatusCode},
9 Bytes,
10};
11#[cfg(any(feature = "json", feature = "xml"))]
12use serde::de::DeserializeOwned;
13use std::{fmt, ops::Deref};
14
15#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct RawResponse {
18 status: StatusCode,
19 headers: Headers,
20 body: ResponseBody,
21}
22
23impl RawResponse {
24 pub fn from_bytes(status: StatusCode, headers: Headers, body: impl Into<Bytes>) -> Self {
26 Self {
27 status,
28 headers,
29 body: ResponseBody::from_bytes(body),
30 }
31 }
32
33 pub fn status(&self) -> StatusCode {
35 self.status
36 }
37
38 pub fn headers(&self) -> &Headers {
40 &self.headers
41 }
42
43 pub fn body(&self) -> &ResponseBody {
45 &self.body
46 }
47
48 pub fn into_body(self) -> ResponseBody {
50 self.body
51 }
52
53 pub fn deconstruct(self) -> (StatusCode, Headers, ResponseBody) {
55 (self.status, self.headers, self.body)
56 }
57}
58
59#[derive(Clone, PartialEq, Eq)]
63pub struct ResponseBody(Bytes);
64
65impl ResponseBody {
66 pub fn from_bytes(bytes: impl Into<Bytes>) -> Self {
68 Self(bytes.into())
69 }
70
71 pub fn into_string(self) -> crate::Result<String> {
73 std::str::from_utf8(&self.0)
74 .with_context(
75 ErrorKind::DataConversion,
76 "response body was not utf-8 like expected",
77 )
78 .map(ToOwned::to_owned)
79 }
80
81 #[cfg(feature = "json")]
83 pub fn json<T>(&self) -> crate::Result<T>
84 where
85 T: DeserializeOwned,
86 {
87 crate::json::from_json(&self.0)
88 }
89
90 #[cfg(feature = "xml")]
92 pub fn xml<T>(&self) -> crate::Result<T>
93 where
94 T: DeserializeOwned,
95 {
96 crate::xml::from_xml(&self.0)
97 }
98}
99
100impl AsRef<[u8]> for ResponseBody {
101 #[inline]
102 fn as_ref(&self) -> &[u8] {
103 &self.0
104 }
105}
106
107impl Deref for ResponseBody {
108 type Target = [u8];
109 #[inline]
110 fn deref(&self) -> &Self::Target {
111 &self.0
112 }
113}
114
115impl fmt::Debug for ResponseBody {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 f.write_str("ResponseBody")
118 }
119}
120
121impl From<ResponseBody> for Bytes {
122 fn from(body: ResponseBody) -> Self {
123 body.0
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130 use serde::Deserialize;
131
132 #[derive(Debug, Deserialize)]
133 struct Employee {
134 id: u32,
135 name: String,
136 }
137
138 #[test]
139 fn as_ref() {
140 let resp =
141 RawResponse::from_bytes(StatusCode::Ok, Headers::new(), r#"{"id":12,"name":"Bob"}"#);
142 assert_eq!(resp.body().as_ref(), br#"{"id":12,"name":"Bob"}"#);
143 }
144
145 #[test]
146 fn deref() {
147 let resp =
148 RawResponse::from_bytes(StatusCode::Ok, Headers::new(), r#"{"id":12,"name":"Bob"}"#);
149 assert_eq!(&*resp.into_body(), br#"{"id":12,"name":"Bob"}"#);
150 }
151
152 #[cfg(feature = "json")]
153 #[test]
154 fn deserializes_json() {
155 let resp =
156 RawResponse::from_bytes(StatusCode::Ok, Headers::new(), r#"{"id":12,"name":"Bob"}"#);
157 let e: Employee = resp.into_body().json().expect("deserialize Employee");
158 assert_eq!(e.id, 12);
159 assert_eq!(e.name, "Bob");
160 }
161
162 #[cfg(feature = "xml")]
163 #[test]
164 fn deserializes_xml() {
165 let resp = RawResponse::from_bytes(
166 StatusCode::Ok,
167 Headers::new(),
168 r#"<Employee><id>34</id><name>Maria</name></Employee>"#,
169 );
170 let e: Employee = resp.into_body().xml().expect("deserialize Employee");
171 assert_eq!(e.id, 34);
172 assert_eq!(e.name, "Maria");
173 }
174}