libcoap_rs/
protocol.rs

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