aws_smithy_eventstream/
smithy.rs1use crate::error::{Error, ErrorKind};
7use aws_smithy_types::event_stream::{Header, HeaderValue, Message};
8use aws_smithy_types::str_bytes::StrBytes;
9use aws_smithy_types::{Blob, DateTime};
10
11macro_rules! expect_shape_fn {
12 (fn $fn_name:ident[$val_typ:ident] -> $result_typ:ident { $val_name:ident -> $val_expr:expr }) => {
13 #[doc = "Expects that `header` is a `"]
14 #[doc = stringify!($result_typ)]
15 #[doc = "`."]
16 pub fn $fn_name(header: &Header) -> Result<$result_typ, Error> {
17 match header.value() {
18 HeaderValue::$val_typ($val_name) => Ok($val_expr),
19 _ => Err(ErrorKind::Unmarshalling(format!(
20 "expected '{}' header value to be {}",
21 header.name().as_str(),
22 stringify!($val_typ)
23 ))
24 .into()),
25 }
26 }
27 };
28}
29
30expect_shape_fn!(fn expect_bool[Bool] -> bool { value -> *value });
31expect_shape_fn!(fn expect_byte[Byte] -> i8 { value -> *value });
32expect_shape_fn!(fn expect_int16[Int16] -> i16 { value -> *value });
33expect_shape_fn!(fn expect_int32[Int32] -> i32 { value -> *value });
34expect_shape_fn!(fn expect_int64[Int64] -> i64 { value -> *value });
35expect_shape_fn!(fn expect_byte_array[ByteArray] -> Blob { bytes -> Blob::new(bytes.as_ref()) });
36expect_shape_fn!(fn expect_string[String] -> String { value -> value.as_str().into() });
37expect_shape_fn!(fn expect_timestamp[Timestamp] -> DateTime { value -> *value });
38
39#[derive(Debug)]
41pub struct ResponseHeaders<'a> {
42 pub content_type: Option<&'a StrBytes>,
50
51 pub message_type: &'a StrBytes,
56
57 pub smithy_type: &'a StrBytes,
61}
62
63impl ResponseHeaders<'_> {
64 pub fn content_type(&self) -> Option<&str> {
66 self.content_type.map(|ct| ct.as_str())
67 }
68}
69
70fn expect_header_str_value<'a>(
71 header: Option<&'a Header>,
72 name: &str,
73) -> Result<&'a StrBytes, Error> {
74 match header {
75 Some(header) => Ok(header.value().as_string().map_err(|value| {
76 Error::from(ErrorKind::Unmarshalling(format!(
77 "expected response {name} header to be string, received {value:?}"
78 )))
79 })?),
80 None => Err(ErrorKind::Unmarshalling(format!(
81 "expected response to include {name} header, but it was missing"
82 ))
83 .into()),
84 }
85}
86
87pub fn parse_response_headers(message: &Message) -> Result<ResponseHeaders<'_>, Error> {
92 let (mut content_type, mut message_type, mut event_type, mut exception_type) =
93 (None, None, None, None);
94 for header in message.headers() {
95 match header.name().as_str() {
96 ":content-type" => content_type = Some(header),
97 ":message-type" => message_type = Some(header),
98 ":event-type" => event_type = Some(header),
99 ":exception-type" => exception_type = Some(header),
100 _ => {}
101 }
102 }
103 let message_type = expect_header_str_value(message_type, ":message-type")?;
104 Ok(ResponseHeaders {
105 content_type: content_type
106 .map(|ct| expect_header_str_value(Some(ct), ":content-type"))
107 .transpose()?,
108 message_type,
109 smithy_type: if message_type.as_str() == "event" {
110 expect_header_str_value(event_type, ":event-type")?
111 } else if message_type.as_str() == "exception" {
112 expect_header_str_value(exception_type, ":exception-type")?
113 } else {
114 return Err(ErrorKind::Unmarshalling(format!(
115 "unrecognized `:message-type`: {}",
116 message_type.as_str()
117 ))
118 .into());
119 },
120 })
121}
122
123#[cfg(test)]
124mod tests {
125 use super::parse_response_headers;
126 use aws_smithy_types::event_stream::{Header, HeaderValue, Message};
127
128 #[test]
129 fn normal_message() {
130 let message = Message::new(&b"test"[..])
131 .add_header(Header::new(
132 ":event-type",
133 HeaderValue::String("Foo".into()),
134 ))
135 .add_header(Header::new(
136 ":content-type",
137 HeaderValue::String("application/json".into()),
138 ))
139 .add_header(Header::new(
140 ":message-type",
141 HeaderValue::String("event".into()),
142 ));
143 let parsed = parse_response_headers(&message).unwrap();
144 assert_eq!("Foo", parsed.smithy_type.as_str());
145 assert_eq!(Some("application/json"), parsed.content_type());
146 assert_eq!("event", parsed.message_type.as_str());
147 }
148
149 #[test]
150 fn error_message() {
151 let message = Message::new(&b"test"[..])
152 .add_header(Header::new(
153 ":exception-type",
154 HeaderValue::String("BadRequestException".into()),
155 ))
156 .add_header(Header::new(
157 ":content-type",
158 HeaderValue::String("application/json".into()),
159 ))
160 .add_header(Header::new(
161 ":message-type",
162 HeaderValue::String("exception".into()),
163 ));
164 let parsed = parse_response_headers(&message).unwrap();
165 assert_eq!("BadRequestException", parsed.smithy_type.as_str());
166 assert_eq!(Some("application/json"), parsed.content_type());
167 assert_eq!("exception", parsed.message_type.as_str());
168 }
169
170 #[test]
171 fn missing_exception_type() {
172 let message = Message::new(&b"test"[..])
173 .add_header(Header::new(
174 ":content-type",
175 HeaderValue::String("application/json".into()),
176 ))
177 .add_header(Header::new(
178 ":message-type",
179 HeaderValue::String("exception".into()),
180 ));
181 let error = parse_response_headers(&message).err().unwrap().to_string();
182 assert_eq!(
183 "failed to unmarshall message: expected response to include :exception-type \
184 header, but it was missing",
185 error
186 );
187 }
188
189 #[test]
190 fn missing_event_type() {
191 let message = Message::new(&b"test"[..])
192 .add_header(Header::new(
193 ":content-type",
194 HeaderValue::String("application/json".into()),
195 ))
196 .add_header(Header::new(
197 ":message-type",
198 HeaderValue::String("event".into()),
199 ));
200 let error = parse_response_headers(&message).err().unwrap().to_string();
201 assert_eq!(
202 "failed to unmarshall message: expected response to include :event-type \
203 header, but it was missing",
204 error
205 );
206 }
207
208 #[test]
209 fn missing_content_type() {
210 let message = Message::new(&b"test"[..])
211 .add_header(Header::new(
212 ":event-type",
213 HeaderValue::String("Foo".into()),
214 ))
215 .add_header(Header::new(
216 ":message-type",
217 HeaderValue::String("event".into()),
218 ));
219 let parsed = parse_response_headers(&message).ok().unwrap();
220 assert_eq!(None, parsed.content_type);
221 assert_eq!("Foo", parsed.smithy_type.as_str());
222 assert_eq!("event", parsed.message_type.as_str());
223 }
224
225 #[test]
226 fn content_type_wrong_type() {
227 let message = Message::new(&b"test"[..])
228 .add_header(Header::new(
229 ":event-type",
230 HeaderValue::String("Foo".into()),
231 ))
232 .add_header(Header::new(":content-type", HeaderValue::Int32(16)))
233 .add_header(Header::new(
234 ":message-type",
235 HeaderValue::String("event".into()),
236 ));
237 let error = parse_response_headers(&message).err().unwrap().to_string();
238 assert_eq!(
239 "failed to unmarshall message: expected response :content-type \
240 header to be string, received Int32(16)",
241 error
242 );
243 }
244}