libcoap_rs/
protocol.rs

1// SPDX-License-Identifier: BSD-2-Clause
2/*
3 * protocol.rs - Types representing CoAP protocol values.
4 * This file is part of the libcoap-rs crate, see the README and LICENSE files for
5 * more information and terms of use.
6 * Copyright © 2021-2023 The NAMIB Project Developers, all rights reserved.
7 * See the README as well as the LICENSE file for more information.
8 */
9
10//! Various types that are specified and defined in the CoAP standard and its extensions.
11
12use std::{
13    ffi::CStr,
14    fmt::{Display, Formatter},
15};
16
17use num_derive::FromPrimitive;
18use num_traits::FromPrimitive;
19
20use libcoap_sys::{
21    coap_option_num_t, coap_pdu_code_t, coap_pdu_type_t,
22    coap_pdu_type_t::{COAP_MESSAGE_ACK, COAP_MESSAGE_CON, COAP_MESSAGE_NON, COAP_MESSAGE_RST},
23    coap_request_t, coap_response_phrase, COAP_MEDIATYPE_ANY, COAP_MEDIATYPE_APPLICATION_CBOR,
24    COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT, COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT0,
25    COAP_MEDIATYPE_APPLICATION_COSE_KEY, COAP_MEDIATYPE_APPLICATION_COSE_KEY_SET, COAP_MEDIATYPE_APPLICATION_COSE_MAC,
26    COAP_MEDIATYPE_APPLICATION_COSE_MAC0, COAP_MEDIATYPE_APPLICATION_COSE_SIGN, COAP_MEDIATYPE_APPLICATION_COSE_SIGN1,
27    COAP_MEDIATYPE_APPLICATION_CWT, COAP_MEDIATYPE_APPLICATION_DOTS_CBOR, COAP_MEDIATYPE_APPLICATION_EXI,
28    COAP_MEDIATYPE_APPLICATION_JSON, COAP_MEDIATYPE_APPLICATION_LINK_FORMAT, COAP_MEDIATYPE_APPLICATION_OCTET_STREAM,
29    COAP_MEDIATYPE_APPLICATION_RDF_XML, COAP_MEDIATYPE_APPLICATION_SENML_CBOR, COAP_MEDIATYPE_APPLICATION_SENML_EXI,
30    COAP_MEDIATYPE_APPLICATION_SENML_JSON, COAP_MEDIATYPE_APPLICATION_SENML_XML,
31    COAP_MEDIATYPE_APPLICATION_SENSML_CBOR, COAP_MEDIATYPE_APPLICATION_SENSML_EXI,
32    COAP_MEDIATYPE_APPLICATION_SENSML_JSON, COAP_MEDIATYPE_APPLICATION_SENSML_XML, COAP_MEDIATYPE_APPLICATION_XML,
33    COAP_MEDIATYPE_TEXT_PLAIN, COAP_OPTION_ACCEPT, COAP_OPTION_BLOCK1, COAP_OPTION_BLOCK2, COAP_OPTION_CONTENT_FORMAT,
34    COAP_OPTION_ETAG, COAP_OPTION_HOP_LIMIT, COAP_OPTION_IF_MATCH, COAP_OPTION_IF_NONE_MATCH,
35    COAP_OPTION_LOCATION_PATH, COAP_OPTION_LOCATION_QUERY, COAP_OPTION_MAXAGE, COAP_OPTION_NORESPONSE,
36    COAP_OPTION_OBSERVE, COAP_OPTION_PROXY_SCHEME, COAP_OPTION_PROXY_URI, COAP_OPTION_SIZE1, COAP_OPTION_SIZE2,
37    COAP_OPTION_URI_HOST, COAP_OPTION_URI_PATH, COAP_OPTION_URI_PORT, COAP_OPTION_URI_QUERY,
38};
39
40use crate::error::{MessageCodeError, UnknownOptionError};
41
42pub type ETag = Box<[u8]>;
43pub type MaxAge = u32;
44pub type LocationPath = String;
45pub type LocationQuery = String;
46pub type UriHost = String;
47pub type UriPort = u16;
48pub type UriPath = String;
49pub type UriQuery = String;
50pub type ContentFormat = u16;
51pub type ProxyUri = String;
52pub type ProxyScheme = String;
53pub type Size = u32;
54pub type Block = u32;
55pub type HopLimit = u16;
56pub type NoResponse = u8;
57pub type Observe = u32;
58
59pub type CoapOptionNum = coap_option_num_t;
60pub type CoapToken = Box<[u8]>;
61
62/// Representation of a CoAP match expression supplied in the If-Match option, see
63/// [RFC 7252, Section 5.10.8.1](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.8.1).
64#[derive(Clone, Debug, Eq, PartialEq, Hash)]
65pub enum CoapMatch {
66    ETag(ETag),
67    Empty,
68}
69
70/// CoAP option types as defined in [RFC 7252, Section 5.10](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10)
71/// and later CoAP extensions.
72///
73/// The enum value corresponds to the appropriate option number and can be retrieved using
74/// `[value] as u16` or [to_raw_option_num()](CoapOptionType::to_raw_option_num()).
75///
76/// See <https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#option-numbers> for a
77/// list of option numbers registered with the IANA.
78#[repr(u16)]
79#[non_exhaustive]
80#[derive(FromPrimitive, Copy, Clone, Debug, PartialEq, Eq, Hash)]
81pub enum CoapOptionType {
82    /// If-Match option ([RFC 7252, Section 5.10.8.1](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.8.1)).
83    IfMatch = COAP_OPTION_IF_MATCH as u16,
84    /// Uri-Host option ([RFC 7252, Section 5.10.1](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.1)).
85    UriHost = COAP_OPTION_URI_HOST as u16,
86    /// ETag option ([RFC 7252, Section 5.10.6](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.6)).
87    ETag = COAP_OPTION_ETAG as u16,
88    /// If-None-Match option ([RFC 7252, Section 5.10.8.2](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.8.2)).
89    IfNoneMatch = COAP_OPTION_IF_NONE_MATCH as u16,
90    /// Uri-Port option ([RFC 7252, Section 5.10.1](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.1)).
91    UriPort = COAP_OPTION_URI_PORT as u16,
92    /// Location-Path option ([RFC 7252, Section 5.10.7](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.7)).
93    LocationPath = COAP_OPTION_LOCATION_PATH as u16,
94    /// Uri-Path option ([RFC 7252, Section 5.10.1](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.1)).
95    UriPath = COAP_OPTION_URI_PATH as u16,
96    /// Content-Format option ([RFC 7252, Section 5.10.3](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.3)).
97    ContentFormat = COAP_OPTION_CONTENT_FORMAT as u16,
98    /// Max-Age option ([RFC 7252, Section 5.10.5](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.5)).
99    MaxAge = COAP_OPTION_MAXAGE as u16,
100    /// Uri-Query option ([RFC 7252, Section 5.10.1](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.1)).
101    UriQuery = COAP_OPTION_URI_QUERY as u16,
102    /// Accept option ([RFC 7252, Section 5.10.4](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.4)).
103    Accept = COAP_OPTION_ACCEPT as u16,
104    /// Location-Query option ([RFC 7252, Section 5.10.7](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.7)).
105    LocationQuery = COAP_OPTION_LOCATION_QUERY as u16,
106    /// Proxy-Uri option ([RFC 7252, Section 5.10.2](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.2)).
107    ProxyUri = COAP_OPTION_PROXY_URI as u16,
108    /// Proxy-Scheme option ([RFC 7252, Section 5.10.2](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.2)).
109    ProxyScheme = COAP_OPTION_PROXY_SCHEME as u16,
110    /// Size1 option ([RFC 7959, Section 4](https://datatracker.ietf.org/doc/html/rfc7959#section-4)).
111    Size1 = COAP_OPTION_SIZE1 as u16,
112    /// Size2 option ([RFC 7959, Section 4](https://datatracker.ietf.org/doc/html/rfc7959#section-4)).
113    Size2 = COAP_OPTION_SIZE2 as u16,
114    /// Block1 option ([RFC 7959, Section 2.1](https://datatracker.ietf.org/doc/html/rfc7959#section-2.1)).
115    Block1 = COAP_OPTION_BLOCK1 as u16,
116    /// Block2 option ([RFC 7959, Section 2.1](https://datatracker.ietf.org/doc/html/rfc7959#section-2.1)).
117    Block2 = COAP_OPTION_BLOCK2 as u16,
118    /// Hop-Limit option ([RFC 8768, Section 3](https://datatracker.ietf.org/doc/html/rfc8768#section-3)).
119    HopLimit = COAP_OPTION_HOP_LIMIT as u16,
120    /// No-Response option ([RFC 7967, Section 2](https://datatracker.ietf.org/doc/html/rfc7967#section-2)).
121    NoResponse = COAP_OPTION_NORESPONSE as u16,
122    /// Observe option ([RFC 7641, Section 2](https://datatracker.ietf.org/doc/html/rfc7641#section-2)).
123    Observe = COAP_OPTION_OBSERVE as u16,
124    // TODO
125    //OsCore = COAP_OPTION_OSCORE as u16,
126}
127
128impl CoapOptionType {
129    /// Returns the option number this type belongs to.
130    pub fn to_raw_option_num(self) -> coap_option_num_t {
131        self as u16
132    }
133
134    /// Returns the maximum size in bytes that a value of this option type should have.
135    pub fn max_len(&self) -> usize {
136        match self {
137            CoapOptionType::IfMatch => 8,
138            CoapOptionType::UriHost => 255,
139            CoapOptionType::ETag => 8,
140            CoapOptionType::IfNoneMatch => 0,
141            CoapOptionType::UriPort => 2,
142            CoapOptionType::LocationPath => 255,
143            CoapOptionType::UriPath => 255,
144            CoapOptionType::ContentFormat => 2,
145            CoapOptionType::MaxAge => 4,
146            CoapOptionType::UriQuery => 255,
147            CoapOptionType::Accept => 2,
148            CoapOptionType::LocationQuery => 255,
149            CoapOptionType::ProxyUri => 1034,
150            CoapOptionType::ProxyScheme => 255,
151            CoapOptionType::Size1 => 4,
152            CoapOptionType::Size2 => 4,
153            //CoapOptionType::OsCore => 3,
154            CoapOptionType::Block1 => 3,
155            CoapOptionType::Block2 => 3,
156            CoapOptionType::HopLimit => 1,
157            CoapOptionType::NoResponse => 1,
158            CoapOptionType::Observe => 3,
159        }
160    }
161
162    /// Returns the minimum size in bytes that a value of this option type should have.
163    pub fn min_len(&self) -> usize {
164        match self {
165            CoapOptionType::IfMatch => 0,
166            CoapOptionType::UriHost => 1,
167            CoapOptionType::ETag => 1,
168            CoapOptionType::IfNoneMatch => 0,
169            CoapOptionType::UriPort => 0,
170            CoapOptionType::LocationPath => 0,
171            CoapOptionType::UriPath => 0,
172            CoapOptionType::ContentFormat => 0,
173            CoapOptionType::MaxAge => 0,
174            CoapOptionType::UriQuery => 0,
175            CoapOptionType::Accept => 0,
176            CoapOptionType::LocationQuery => 0,
177            CoapOptionType::ProxyUri => 1,
178            CoapOptionType::ProxyScheme => 1,
179            CoapOptionType::Size1 => 0,
180            CoapOptionType::Size2 => 0,
181            //CoapOptionType::OsCore => {},
182            CoapOptionType::Block1 => 0,
183            CoapOptionType::Block2 => 0,
184            CoapOptionType::HopLimit => 1,
185            CoapOptionType::NoResponse => 0,
186            CoapOptionType::Observe => 0,
187        }
188    }
189}
190
191impl TryFrom<coap_option_num_t> for CoapOptionType {
192    type Error = UnknownOptionError;
193
194    fn try_from(num: coap_option_num_t) -> Result<Self, Self::Error> {
195        <CoapOptionType as FromPrimitive>::from_u16(num).ok_or(UnknownOptionError::Unknown)
196    }
197}
198
199/// Various content formats that can be used for CoAP requests.
200///
201/// To get the corresponding numeric value, use `[value] as u16`.
202///
203/// See <https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#content-formats> for
204/// values that are currently registered with the IANA.
205#[repr(u16)]
206#[derive(Copy, Clone, FromPrimitive, Eq, PartialEq, Hash, Debug)]
207#[non_exhaustive]
208pub enum CoapContentFormat {
209    Any = COAP_MEDIATYPE_ANY as u16,
210    Cbor = COAP_MEDIATYPE_APPLICATION_CBOR as u16,
211    DotsCbor = COAP_MEDIATYPE_APPLICATION_DOTS_CBOR as u16,
212    SenMlCbor = COAP_MEDIATYPE_APPLICATION_SENML_CBOR as u16,
213    SenMlExi = COAP_MEDIATYPE_APPLICATION_SENML_EXI as u16,
214    CoseEncrypt = COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT as u16,
215    CoseEncrypt0 = COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT0 as u16,
216    CoseKey = COAP_MEDIATYPE_APPLICATION_COSE_KEY as u16,
217    CoseKeySet = COAP_MEDIATYPE_APPLICATION_COSE_KEY_SET as u16,
218    CoseMac = COAP_MEDIATYPE_APPLICATION_COSE_MAC as u16,
219    CoseMac0 = COAP_MEDIATYPE_APPLICATION_COSE_MAC0 as u16,
220    CoseSign = COAP_MEDIATYPE_APPLICATION_COSE_SIGN as u16,
221    CoseSign1 = COAP_MEDIATYPE_APPLICATION_COSE_SIGN1 as u16,
222    Cwt = COAP_MEDIATYPE_APPLICATION_CWT as u16,
223    Exi = COAP_MEDIATYPE_APPLICATION_EXI as u16,
224    Json = COAP_MEDIATYPE_APPLICATION_JSON as u16,
225    LinkFormat = COAP_MEDIATYPE_APPLICATION_LINK_FORMAT as u16,
226    OctetStream = COAP_MEDIATYPE_APPLICATION_OCTET_STREAM as u16,
227    RdfXml = COAP_MEDIATYPE_APPLICATION_RDF_XML as u16,
228    SenMlJson = COAP_MEDIATYPE_APPLICATION_SENML_JSON as u16,
229    SenMlXml = COAP_MEDIATYPE_APPLICATION_SENML_XML as u16,
230    SensMlCbor = COAP_MEDIATYPE_APPLICATION_SENSML_CBOR as u16,
231    SensMlExi = COAP_MEDIATYPE_APPLICATION_SENSML_EXI as u16,
232    SensMlJson = COAP_MEDIATYPE_APPLICATION_SENSML_JSON as u16,
233    SensMlXml = COAP_MEDIATYPE_APPLICATION_SENSML_XML as u16,
234    ApplicationXml = COAP_MEDIATYPE_APPLICATION_XML as u16,
235    TextPlain = COAP_MEDIATYPE_TEXT_PLAIN as u16,
236    Other,
237}
238
239impl From<ContentFormat> for CoapContentFormat {
240    fn from(value: u16) -> Self {
241        <CoapContentFormat as FromPrimitive>::from_u16(value).unwrap_or(CoapContentFormat::Other)
242    }
243}
244
245/// Representation of a CoAP message code.
246/// Can be a request code, a response code, or the empty message code.
247///
248/// The numeric value (that can also be obtained with [to_raw_request()](CoapRequestCode::to_raw_pdu_code()))
249/// corresponds to the values defined in <https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#codes>.
250#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
251#[repr(u8)]
252pub enum CoapMessageCode {
253    Empty,
254    Request(CoapRequestCode),
255    Response(CoapResponseCode),
256}
257
258impl CoapMessageCode {
259    /// Returns the corresponding raw code for this message code, which can be added to a raw
260    /// [coap_pdu_t](libcoap_sys::coap_pdu_t).
261    pub fn to_raw_pdu_code(self) -> coap_pdu_code_t {
262        match self {
263            CoapMessageCode::Empty => coap_pdu_code_t::COAP_EMPTY_CODE,
264            CoapMessageCode::Request(req) => req.to_raw_pdu_code(),
265            CoapMessageCode::Response(rsp) => rsp.to_raw_pdu_code(),
266        }
267    }
268}
269
270impl From<CoapRequestCode> for CoapMessageCode {
271    fn from(code: CoapRequestCode) -> Self {
272        CoapMessageCode::Request(code)
273    }
274}
275
276impl From<CoapResponseCode> for CoapMessageCode {
277    fn from(code: CoapResponseCode) -> Self {
278        CoapMessageCode::Response(code)
279    }
280}
281
282impl TryFrom<coap_pdu_code_t> for CoapMessageCode {
283    type Error = MessageCodeError;
284
285    fn try_from(code: coap_pdu_code_t) -> Result<Self, Self::Error> {
286        match code {
287            coap_pdu_code_t::COAP_EMPTY_CODE => Ok(CoapMessageCode::Empty),
288            code => CoapRequestCode::try_from(code)
289                .map(CoapMessageCode::Request)
290                .or_else(|_| CoapResponseCode::try_from(code).map(CoapMessageCode::Response)),
291        }
292    }
293}
294
295/// Representation of a CoAP request/method code.
296///
297/// See <https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#method-codes> for the
298/// values currently registered with the IANA.
299#[repr(u8)]
300#[non_exhaustive]
301#[derive(FromPrimitive, Clone, Copy, Eq, PartialEq, Hash, Debug)]
302pub enum CoapRequestCode {
303    Get = coap_pdu_code_t::COAP_REQUEST_CODE_GET as u8,
304    Put = coap_pdu_code_t::COAP_REQUEST_CODE_PUT as u8,
305    Delete = coap_pdu_code_t::COAP_REQUEST_CODE_DELETE as u8,
306    Post = coap_pdu_code_t::COAP_REQUEST_CODE_POST as u8,
307    Fetch = coap_pdu_code_t::COAP_REQUEST_CODE_FETCH as u8,
308    IPatch = coap_pdu_code_t::COAP_REQUEST_CODE_IPATCH as u8,
309    Patch = coap_pdu_code_t::COAP_REQUEST_CODE_PATCH as u8,
310}
311
312impl CoapRequestCode {
313    /// Returns the [coap_request_t](libcoap_sys::coap_request_t) corresponding to this request code.
314    ///
315    /// Note that this is *not* the code that should be set inside of a [coap_pdu_t](libcoap_sys::coap_pdu_t),
316    /// but a value used internally by the libcoap C library. See [to_raw_pdu_code()](CoapRequestCode::to_raw_pdu_code())
317    /// for the standardized value used in messages.
318    pub fn to_raw_request(self) -> coap_request_t {
319        match self {
320            CoapRequestCode::Get => coap_request_t::COAP_REQUEST_GET,
321            CoapRequestCode::Put => coap_request_t::COAP_REQUEST_PUT,
322            CoapRequestCode::Delete => coap_request_t::COAP_REQUEST_FETCH,
323            CoapRequestCode::Post => coap_request_t::COAP_REQUEST_POST,
324            CoapRequestCode::Fetch => coap_request_t::COAP_REQUEST_FETCH,
325            CoapRequestCode::IPatch => coap_request_t::COAP_REQUEST_IPATCH,
326            CoapRequestCode::Patch => coap_request_t::COAP_REQUEST_PATCH,
327        }
328    }
329
330    /// Returns the raw [coap_pdu_code_t](libcoap_sys::coap_pdu_code_t) corresponding to this
331    /// request code.
332    pub fn to_raw_pdu_code(self) -> coap_pdu_code_t {
333        match self {
334            CoapRequestCode::Get => coap_pdu_code_t::COAP_REQUEST_CODE_GET,
335            CoapRequestCode::Put => coap_pdu_code_t::COAP_REQUEST_CODE_PUT,
336            CoapRequestCode::Delete => coap_pdu_code_t::COAP_REQUEST_CODE_FETCH,
337            CoapRequestCode::Post => coap_pdu_code_t::COAP_REQUEST_CODE_POST,
338            CoapRequestCode::Fetch => coap_pdu_code_t::COAP_REQUEST_CODE_FETCH,
339            CoapRequestCode::IPatch => coap_pdu_code_t::COAP_REQUEST_CODE_IPATCH,
340            CoapRequestCode::Patch => coap_pdu_code_t::COAP_REQUEST_CODE_PATCH,
341        }
342    }
343}
344
345impl From<coap_request_t> for CoapRequestCode {
346    fn from(req: coap_request_t) -> Self {
347        match req {
348            coap_request_t::COAP_REQUEST_GET => CoapRequestCode::Get,
349            coap_request_t::COAP_REQUEST_POST => CoapRequestCode::Post,
350            coap_request_t::COAP_REQUEST_PUT => CoapRequestCode::Put,
351            coap_request_t::COAP_REQUEST_DELETE => CoapRequestCode::Delete,
352            coap_request_t::COAP_REQUEST_FETCH => CoapRequestCode::Fetch,
353            coap_request_t::COAP_REQUEST_PATCH => CoapRequestCode::Patch,
354            coap_request_t::COAP_REQUEST_IPATCH => CoapRequestCode::IPatch,
355            _ => panic!("unknown request type"),
356        }
357    }
358}
359
360impl TryFrom<coap_pdu_code_t> for CoapRequestCode {
361    type Error = MessageCodeError;
362
363    fn try_from(req: coap_pdu_code_t) -> Result<Self, Self::Error> {
364        <CoapRequestCode as FromPrimitive>::from_u32(req as u32).ok_or(MessageCodeError::NotARequestCode)
365    }
366}
367
368/// Representation of a CoAP response code.
369///
370/// See <https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#response-codes> for
371/// the possible values currently registered with the IANA.
372#[repr(u8)]
373#[non_exhaustive]
374#[derive(Clone, Copy, FromPrimitive, Debug, Eq, PartialEq, Hash)]
375pub enum CoapResponseCode {
376    Content = coap_pdu_code_t::COAP_RESPONSE_CODE_CONTENT as u8,
377    BadGateway = coap_pdu_code_t::COAP_RESPONSE_CODE_BAD_GATEWAY as u8,
378    Continue = coap_pdu_code_t::COAP_RESPONSE_CODE_CONTINUE as u8,
379    Conflict = coap_pdu_code_t::COAP_RESPONSE_CODE_CONFLICT as u8,
380    BadRequest = coap_pdu_code_t::COAP_RESPONSE_CODE_BAD_REQUEST as u8,
381    BadOption = coap_pdu_code_t::COAP_RESPONSE_CODE_BAD_OPTION as u8,
382    Changed = coap_pdu_code_t::COAP_RESPONSE_CODE_CHANGED as u8,
383    Created = coap_pdu_code_t::COAP_RESPONSE_CODE_CREATED as u8,
384    Deleted = coap_pdu_code_t::COAP_RESPONSE_CODE_DELETED as u8,
385    Forbidden = coap_pdu_code_t::COAP_RESPONSE_CODE_FORBIDDEN as u8,
386    GatewayTimeout = coap_pdu_code_t::COAP_RESPONSE_CODE_GATEWAY_TIMEOUT as u8,
387    HopLimitReached = coap_pdu_code_t::COAP_RESPONSE_CODE_HOP_LIMIT_REACHED as u8,
388    Incomplete = coap_pdu_code_t::COAP_RESPONSE_CODE_INCOMPLETE as u8,
389    InternalError = coap_pdu_code_t::COAP_RESPONSE_CODE_INTERNAL_ERROR as u8,
390    NotAcceptable = coap_pdu_code_t::COAP_RESPONSE_CODE_NOT_ACCEPTABLE as u8,
391    NotAllowed = coap_pdu_code_t::COAP_RESPONSE_CODE_NOT_ALLOWED as u8,
392    NotFound = coap_pdu_code_t::COAP_RESPONSE_CODE_NOT_FOUND as u8,
393    NotImplemented = coap_pdu_code_t::COAP_RESPONSE_CODE_NOT_IMPLEMENTED as u8,
394    PreconditionFailed = coap_pdu_code_t::COAP_RESPONSE_CODE_PRECONDITION_FAILED as u8,
395    ProxyingNotSupported = coap_pdu_code_t::COAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED as u8,
396    RequestTooLarge = coap_pdu_code_t::COAP_RESPONSE_CODE_REQUEST_TOO_LARGE as u8,
397    ServiceUnavailable = coap_pdu_code_t::COAP_RESPONSE_CODE_SERVICE_UNAVAILABLE as u8,
398    TooManyRequests = coap_pdu_code_t::COAP_RESPONSE_CODE_TOO_MANY_REQUESTS as u8,
399    Unauthorized = coap_pdu_code_t::COAP_RESPONSE_CODE_UNAUTHORIZED as u8,
400    Unprocessable = coap_pdu_code_t::COAP_RESPONSE_CODE_UNPROCESSABLE as u8,
401    UnsupportedContentFormat = coap_pdu_code_t::COAP_RESPONSE_CODE_UNSUPPORTED_CONTENT_FORMAT as u8,
402    Valid = coap_pdu_code_t::COAP_RESPONSE_CODE_VALID as u8,
403}
404
405impl CoapResponseCode {
406    /// Returns the raw [coap_pdu_code_t](libcoap_sys::coap_pdu_code_t) corresponding to this
407    /// request code.
408    pub fn to_raw_pdu_code(self) -> coap_pdu_code_t {
409        match self {
410            CoapResponseCode::Content => coap_pdu_code_t::COAP_RESPONSE_CODE_CONTENT,
411            CoapResponseCode::BadGateway => coap_pdu_code_t::COAP_RESPONSE_CODE_BAD_GATEWAY,
412            CoapResponseCode::Continue => coap_pdu_code_t::COAP_RESPONSE_CODE_CONTINUE,
413            CoapResponseCode::Conflict => coap_pdu_code_t::COAP_RESPONSE_CODE_CONFLICT,
414            CoapResponseCode::BadRequest => coap_pdu_code_t::COAP_RESPONSE_CODE_BAD_REQUEST,
415            CoapResponseCode::BadOption => coap_pdu_code_t::COAP_RESPONSE_CODE_BAD_OPTION,
416            CoapResponseCode::Changed => coap_pdu_code_t::COAP_RESPONSE_CODE_CHANGED,
417            CoapResponseCode::Created => coap_pdu_code_t::COAP_RESPONSE_CODE_CREATED,
418            CoapResponseCode::Deleted => coap_pdu_code_t::COAP_RESPONSE_CODE_DELETED,
419            CoapResponseCode::Forbidden => coap_pdu_code_t::COAP_RESPONSE_CODE_FORBIDDEN,
420            CoapResponseCode::GatewayTimeout => coap_pdu_code_t::COAP_RESPONSE_CODE_GATEWAY_TIMEOUT,
421            CoapResponseCode::HopLimitReached => coap_pdu_code_t::COAP_RESPONSE_CODE_HOP_LIMIT_REACHED,
422            CoapResponseCode::Incomplete => coap_pdu_code_t::COAP_RESPONSE_CODE_INCOMPLETE,
423            CoapResponseCode::InternalError => coap_pdu_code_t::COAP_RESPONSE_CODE_INTERNAL_ERROR,
424            CoapResponseCode::NotAcceptable => coap_pdu_code_t::COAP_RESPONSE_CODE_NOT_ACCEPTABLE,
425            CoapResponseCode::NotAllowed => coap_pdu_code_t::COAP_RESPONSE_CODE_NOT_ALLOWED,
426            CoapResponseCode::NotFound => coap_pdu_code_t::COAP_RESPONSE_CODE_NOT_FOUND,
427            CoapResponseCode::NotImplemented => coap_pdu_code_t::COAP_RESPONSE_CODE_NOT_IMPLEMENTED,
428            CoapResponseCode::PreconditionFailed => coap_pdu_code_t::COAP_RESPONSE_CODE_PRECONDITION_FAILED,
429            CoapResponseCode::ProxyingNotSupported => coap_pdu_code_t::COAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED,
430            CoapResponseCode::RequestTooLarge => coap_pdu_code_t::COAP_RESPONSE_CODE_REQUEST_TOO_LARGE,
431            CoapResponseCode::ServiceUnavailable => coap_pdu_code_t::COAP_RESPONSE_CODE_SERVICE_UNAVAILABLE,
432            CoapResponseCode::TooManyRequests => coap_pdu_code_t::COAP_RESPONSE_CODE_TOO_MANY_REQUESTS,
433            CoapResponseCode::Unauthorized => coap_pdu_code_t::COAP_RESPONSE_CODE_UNAUTHORIZED,
434            CoapResponseCode::Unprocessable => coap_pdu_code_t::COAP_RESPONSE_CODE_UNPROCESSABLE,
435            CoapResponseCode::UnsupportedContentFormat => {
436                coap_pdu_code_t::COAP_RESPONSE_CODE_UNSUPPORTED_CONTENT_FORMAT
437            },
438            CoapResponseCode::Valid => coap_pdu_code_t::COAP_RESPONSE_CODE_VALID,
439        }
440    }
441}
442
443impl Display for CoapResponseCode {
444    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
445        let response_phrase = unsafe {
446            let raw_phrase = coap_response_phrase(*self as u8);
447            if raw_phrase.is_null() {
448                "unknown response code"
449            } else {
450                CStr::from_ptr(raw_phrase)
451                    .to_str()
452                    .unwrap_or("unable to retrieve phrase for response code")
453            }
454        };
455
456        write!(f, "{}", response_phrase)
457    }
458}
459
460impl TryFrom<coap_pdu_code_t> for CoapResponseCode {
461    type Error = MessageCodeError;
462
463    fn try_from(value: coap_pdu_code_t) -> Result<Self, Self::Error> {
464        <CoapResponseCode as FromPrimitive>::from_u32(value as u32).ok_or(MessageCodeError::NotAResponseCode)
465    }
466}
467
468/// CoAP message types as defined in [RFC 7252, Section 3](https://datatracker.ietf.org/doc/html/rfc7252#section-3)
469/// and described in [RFC 7252, Section 4.2 and 4.3](https://datatracker.ietf.org/doc/html/rfc7252#section-4.2).
470#[repr(u8)]
471#[derive(Copy, Clone, Hash, Eq, PartialEq, FromPrimitive, Debug)]
472pub enum CoapMessageType {
473    /// Confirmable message, i.e. a message whose reception should be confirmed by the peer.
474    Con = COAP_MESSAGE_CON as u8,
475    /// Non-confirmable message, i.e. a message whose reception should not be confirmed by the peer.
476    Non = COAP_MESSAGE_NON as u8,
477    /// Acknowledgement for a previous message.
478    Ack = COAP_MESSAGE_ACK as u8,
479    /// Non-acknowledgement for a previous message.
480    Rst = COAP_MESSAGE_RST as u8,
481}
482
483impl CoapMessageType {
484    /// Returns the corresponding raw [coap_pdu_type_t](libcoap_sys::coap_pdu_type_t) instance for
485    /// this message type.
486    pub fn to_raw_pdu_type(&self) -> coap_pdu_type_t {
487        match self {
488            CoapMessageType::Con => COAP_MESSAGE_CON,
489            CoapMessageType::Non => COAP_MESSAGE_NON,
490            CoapMessageType::Ack => COAP_MESSAGE_ACK,
491            CoapMessageType::Rst => COAP_MESSAGE_RST,
492        }
493    }
494}
495
496impl From<coap_pdu_type_t> for CoapMessageType {
497    fn from(raw_type: coap_pdu_type_t) -> Self {
498        num_traits::FromPrimitive::from_u32(raw_type as u32).expect("unknown PDU type")
499    }
500}