1use crate::error::{MessageConversionError, MessageTypeError, OptionValueError};
11use crate::message::{CoapMessage, CoapMessageCommon, CoapOption};
12use crate::protocol::{
13 CoapMessageCode, CoapMessageType, CoapOptionType, CoapResponseCode, ContentFormat, ETag, MaxAge, Observe,
14};
15use crate::types::CoapUri;
16use std::fmt::Display;
17use std::fmt::Formatter;
18
19#[derive(Clone, Debug, Eq, PartialEq, Hash)]
21pub struct CoapResponseLocation(CoapUri);
22
23impl Display for CoapResponseLocation {
24 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
25 f.write_fmt(format_args!("Response Location: {}", self.0))
26 }
27}
28
29impl CoapResponseLocation {
30 pub fn new_response_location(uri: CoapUri) -> Result<CoapResponseLocation, OptionValueError> {
33 if uri.scheme().is_some() || uri.host().is_some() || uri.port().is_some() {
34 return Err(OptionValueError::IllegalValue);
35 }
36 Ok(CoapResponseLocation(uri))
37 }
38
39 pub fn into_options(self) -> Vec<CoapOption> {
41 let mut options = Vec::new();
42 let mut uri = self.0;
43 if let Some(path) = uri.drain_path_iter() {
44 options.extend(path.map(CoapOption::LocationPath));
45 }
46 if let Some(query) = uri.drain_query_iter() {
47 options.extend(query.map(CoapOption::LocationQuery));
48 }
49 options
50 }
51
52 pub fn as_uri(&self) -> &CoapUri {
54 &self.0
55 }
56}
57
58impl TryFrom<CoapUri> for CoapResponseLocation {
59 type Error = OptionValueError;
60
61 fn try_from(value: CoapUri) -> Result<Self, Self::Error> {
62 CoapResponseLocation::new_response_location(value)
63 }
64}
65
66#[derive(Debug, Clone, Eq, PartialEq, Hash)]
67pub struct CoapResponse {
68 pdu: CoapMessage,
69 content_format: Option<ContentFormat>,
70 max_age: Option<MaxAge>,
71 etag: Option<ETag>,
72 location: Option<CoapResponseLocation>,
73 observe: Option<Observe>,
74}
75
76impl CoapResponse {
77 pub fn new(type_: CoapMessageType, code: CoapResponseCode) -> Result<CoapResponse, MessageTypeError> {
82 match type_ {
83 CoapMessageType::Con | CoapMessageType::Non | CoapMessageType::Ack => {},
84 v => return Err(MessageTypeError::InvalidForMessageCode(v)),
85 }
86 Ok(CoapResponse {
87 pdu: CoapMessage::new(type_, code.into()),
88 content_format: None,
89 max_age: None,
90 etag: None,
91 location: None,
92 observe: None,
93 })
94 }
95
96 pub fn max_age(&self) -> Option<MaxAge> {
98 self.max_age
99 }
100
101 pub fn set_max_age(&mut self, max_age: Option<MaxAge>) {
108 self.max_age = max_age
109 }
110
111 pub fn content_format(&self) -> Option<ContentFormat> {
113 self.content_format
114 }
115
116 pub fn set_content_format(&mut self, content_format: Option<ContentFormat>) {
123 self.content_format = content_format;
124 }
125
126 pub fn etag(&self) -> Option<&ETag> {
128 self.etag.as_ref()
129 }
130
131 pub fn set_etag(&mut self, etag: Option<ETag>) {
142 self.etag = etag
143 }
144
145 pub fn observe(&self) -> Option<Observe> {
147 self.observe
148 }
149
150 pub fn set_observe(&mut self, observe: Option<Observe>) {
158 self.observe = observe;
159 }
160
161 pub fn location(&self) -> Option<&CoapResponseLocation> {
163 self.location.as_ref()
164 }
165
166 pub fn set_location<U: Into<CoapUri>>(&mut self, uri: Option<U>) -> Result<(), OptionValueError> {
179 let uri = uri.map(Into::into);
180 if let Some(uri) = uri {
181 self.location = Some(CoapResponseLocation::new_response_location(uri)?)
182 }
183 Ok(())
184 }
185
186 pub fn into_message(mut self) -> CoapMessage {
188 if let Some(loc) = self.location {
189 loc.into_options().into_iter().for_each(|v| self.pdu.add_option(v));
190 }
191 if let Some(max_age) = self.max_age {
192 self.pdu.add_option(CoapOption::MaxAge(max_age));
193 }
194 if let Some(content_format) = self.content_format {
195 self.pdu.add_option(CoapOption::ContentFormat(content_format));
196 }
197 if let Some(etag) = self.etag {
198 self.pdu.add_option(CoapOption::ETag(etag));
199 }
200 if let Some(observe) = self.observe {
201 self.pdu.add_option(CoapOption::Observe(observe));
202 }
203 self.pdu
204 }
205
206 pub fn from_message(pdu: CoapMessage) -> Result<CoapResponse, MessageConversionError> {
210 let mut location_path = None;
211 let mut location_query = None;
212 let mut max_age = None;
213 let mut etag = None;
214 let mut observe = None;
215 let mut content_format = None;
216 let mut additional_opts = Vec::new();
217 for option in pdu.options_iter() {
218 match option {
219 CoapOption::LocationPath(value) => {
220 if location_path.is_none() {
221 location_path = Some(Vec::new());
222 }
223 location_path.as_mut().unwrap().push(value.clone());
224 },
225 CoapOption::LocationQuery(value) => {
226 if location_query.is_none() {
227 location_query = Some(Vec::new());
228 }
229 location_query.as_mut().unwrap().push(value.clone());
230 },
231 CoapOption::ETag(value) => {
232 if etag.is_some() {
233 return Err(MessageConversionError::NonRepeatableOptionRepeated(
234 CoapOptionType::ETag,
235 ));
236 }
237 etag = Some(value.clone());
238 },
239 CoapOption::MaxAge(value) => {
240 if max_age.is_some() {
241 return Err(MessageConversionError::NonRepeatableOptionRepeated(
242 CoapOptionType::MaxAge,
243 ));
244 }
245 max_age = Some(*value);
246 },
247 CoapOption::Observe(value) => {
248 if observe.is_some() {
249 return Err(MessageConversionError::NonRepeatableOptionRepeated(
250 CoapOptionType::Observe,
251 ));
252 }
253 observe = Some(*value)
254 },
255 CoapOption::IfMatch(_) => {
256 return Err(MessageConversionError::InvalidOptionForMessageType(
257 CoapOptionType::IfMatch,
258 ));
259 },
260 CoapOption::IfNoneMatch => {
261 return Err(MessageConversionError::InvalidOptionForMessageType(
262 CoapOptionType::IfNoneMatch,
263 ));
264 },
265 CoapOption::UriHost(_) => {
266 return Err(MessageConversionError::InvalidOptionForMessageType(
267 CoapOptionType::UriHost,
268 ));
269 },
270 CoapOption::UriPort(_) => {
271 return Err(MessageConversionError::InvalidOptionForMessageType(
272 CoapOptionType::UriPort,
273 ));
274 },
275 CoapOption::UriPath(_) => {
276 return Err(MessageConversionError::InvalidOptionForMessageType(
277 CoapOptionType::UriPath,
278 ));
279 },
280 CoapOption::UriQuery(_) => {
281 return Err(MessageConversionError::InvalidOptionForMessageType(
282 CoapOptionType::UriQuery,
283 ));
284 },
285 CoapOption::ProxyUri(_) => {
286 return Err(MessageConversionError::InvalidOptionForMessageType(
287 CoapOptionType::ProxyUri,
288 ));
289 },
290 CoapOption::ProxyScheme(_) => {
291 return Err(MessageConversionError::InvalidOptionForMessageType(
292 CoapOptionType::ProxyScheme,
293 ));
294 },
295 CoapOption::ContentFormat(value) => {
296 if content_format.is_some() {
297 return Err(MessageConversionError::NonRepeatableOptionRepeated(
298 CoapOptionType::ContentFormat,
299 ));
300 }
301 content_format = Some(*value)
302 },
303 CoapOption::Accept(_) => {
304 return Err(MessageConversionError::InvalidOptionForMessageType(
305 CoapOptionType::Accept,
306 ));
307 },
308 CoapOption::Size1(_) => {
309 return Err(MessageConversionError::InvalidOptionForMessageType(
310 CoapOptionType::Size1,
311 ));
312 },
313 CoapOption::Size2(_) => {},
314 CoapOption::Block1(_) => {
315 return Err(MessageConversionError::InvalidOptionForMessageType(
316 CoapOptionType::Block1,
317 ));
318 },
319 CoapOption::Block2(_) => {},
320 CoapOption::HopLimit(_) => {
321 return Err(MessageConversionError::InvalidOptionForMessageType(
322 CoapOptionType::HopLimit,
323 ));
324 },
325 CoapOption::NoResponse(_) => {
326 return Err(MessageConversionError::InvalidOptionForMessageType(
327 CoapOptionType::NoResponse,
328 ));
329 },
330 CoapOption::Other(n, v) => additional_opts.push(CoapOption::Other(*n, v.clone())),
331 }
332 }
333 let location = if location_path.is_some() || location_query.is_some() {
334 Some(
335 CoapResponseLocation::new_response_location(CoapUri::new(
336 None,
337 None,
338 None,
339 location_path,
340 location_query,
341 ))
342 .map_err(|e| MessageConversionError::InvalidOptionValue(None, e))?,
343 )
344 } else {
345 None
346 };
347 Ok(CoapResponse {
348 pdu,
349 content_format,
350 max_age,
351 etag,
352 location,
353 observe,
354 })
355 }
356}
357
358impl CoapMessageCommon for CoapResponse {
359 fn set_code<C: Into<CoapMessageCode>>(&mut self, code: C) {
364 match code.into() {
365 CoapMessageCode::Response(req) => self.pdu.set_code(CoapMessageCode::Response(req)),
366 CoapMessageCode::Request(_) | CoapMessageCode::Empty => {
367 panic!("attempted to set message code of response to value that is not a response code")
368 },
369 }
370 }
371
372 fn as_message(&self) -> &CoapMessage {
373 &self.pdu
374 }
375
376 fn as_message_mut(&mut self) -> &mut CoapMessage {
377 &mut self.pdu
378 }
379}