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

            
12
use std::{
13
    ffi::CStr,
14
    fmt::{Display, Formatter},
15
};
16

            
17
use num_derive::FromPrimitive;
18
use num_traits::FromPrimitive;
19

            
20
use libcoap_sys::{
21
    COAP_MEDIATYPE_APPLICATION_ACE_CBOR, COAP_MEDIATYPE_APPLICATION_CBOR, COAP_MEDIATYPE_APPLICATION_COAP_GROUP_JSON,
22
    COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT,
23
    COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT0, COAP_MEDIATYPE_APPLICATION_COSE_KEY, COAP_MEDIATYPE_APPLICATION_COSE_KEY_SET, COAP_MEDIATYPE_APPLICATION_COSE_MAC,
24
    COAP_MEDIATYPE_APPLICATION_COSE_MAC0, COAP_MEDIATYPE_APPLICATION_COSE_SIGN,
25
    COAP_MEDIATYPE_APPLICATION_COSE_SIGN1, COAP_MEDIATYPE_APPLICATION_CWT,
26
    COAP_MEDIATYPE_APPLICATION_DOTS_CBOR, COAP_MEDIATYPE_APPLICATION_EXI, COAP_MEDIATYPE_APPLICATION_JSON,
27
    COAP_MEDIATYPE_APPLICATION_LINK_FORMAT, COAP_MEDIATYPE_APPLICATION_MB_CBOR_SEQ, COAP_MEDIATYPE_APPLICATION_OCTET_STREAM,
28
    COAP_MEDIATYPE_APPLICATION_OSCORE, COAP_MEDIATYPE_APPLICATION_RDF_XML, COAP_MEDIATYPE_APPLICATION_SENML_CBOR,
29
    COAP_MEDIATYPE_APPLICATION_SENML_EXI, COAP_MEDIATYPE_APPLICATION_SENML_JSON,
30
    COAP_MEDIATYPE_APPLICATION_SENML_XML, COAP_MEDIATYPE_APPLICATION_SENSML_CBOR, COAP_MEDIATYPE_APPLICATION_SENSML_EXI,
31
    COAP_MEDIATYPE_APPLICATION_SENSML_JSON, COAP_MEDIATYPE_APPLICATION_SENSML_XML, COAP_MEDIATYPE_APPLICATION_XML,
32
    COAP_MEDIATYPE_TEXT_PLAIN, COAP_OPTION_ACCEPT,
33
    COAP_OPTION_BLOCK1, COAP_OPTION_BLOCK2,
34
    COAP_OPTION_CONTENT_FORMAT, COAP_OPTION_ECHO, COAP_OPTION_ETAG,
35
    COAP_OPTION_HOP_LIMIT, COAP_OPTION_IF_MATCH, COAP_OPTION_IF_NONE_MATCH, COAP_OPTION_LOCATION_PATH, COAP_OPTION_LOCATION_QUERY,
36
    COAP_OPTION_MAXAGE, COAP_OPTION_NORESPONSE, coap_option_num_t, COAP_OPTION_OBSERVE,
37
    COAP_OPTION_OSCORE, COAP_OPTION_PROXY_SCHEME, COAP_OPTION_PROXY_URI, COAP_OPTION_Q_BLOCK1,
38
    COAP_OPTION_Q_BLOCK2, COAP_OPTION_RTAG, COAP_OPTION_SIZE1, COAP_OPTION_SIZE2, COAP_OPTION_URI_HOST,
39
    COAP_OPTION_URI_PATH, COAP_OPTION_URI_PORT, COAP_OPTION_URI_QUERY, coap_pdu_code_t, coap_pdu_type_t,
40
    coap_pdu_type_t::{COAP_MESSAGE_ACK, COAP_MESSAGE_CON, COAP_MESSAGE_NON, COAP_MESSAGE_RST}, coap_request_t, coap_response_phrase,
41
};
42

            
43
use crate::error::{MessageCodeError, UnknownOptionError};
44

            
45
pub type ETag = Box<[u8]>;
46
pub type MaxAge = u32;
47
pub type LocationPath = String;
48
pub type LocationQuery = String;
49
pub type UriHost = String;
50
pub type UriPort = u16;
51
pub type UriPath = String;
52
pub type UriQuery = String;
53
pub type ContentFormat = u16;
54
pub type ProxyUri = String;
55
pub type ProxyScheme = String;
56
pub type Size = u32;
57
pub type Block = u32;
58
pub type HopLimit = u16;
59
pub type NoResponse = u8;
60
pub type Observe = u32;
61
// TODO actually parse this option (for OSCORE support)
62
pub type Oscore = Box<[u8]>;
63
pub type Echo = Box<[u8]>;
64
pub type RequestTag = Box<[u8]>;
65

            
66
pub type CoapOptionNum = coap_option_num_t;
67
pub type CoapToken = Box<[u8]>;
68

            
69
/// Representation of a CoAP match expression supplied in the If-Match option, see
70
/// [RFC 7252, Section 5.10.8.1](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.8.1).
71
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
72
pub enum CoapMatch {
73
    ETag(ETag),
74
    Empty,
75
}
76

            
77
/// CoAP option types as defined in [RFC 7252, Section 5.10](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10)
78
/// and later CoAP extensions.
79
///
80
/// The enum value corresponds to the appropriate option number and can be retrieved using
81
/// `[value] as u16` or [to_raw_option_num()](CoapOptionType::to_raw_option_num()).
82
///
83
/// See <https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#option-numbers> for a
84
/// list of option numbers registered with the IANA.
85
#[repr(u16)]
86
#[non_exhaustive]
87
932
#[derive(FromPrimitive, Copy, Clone, Debug, PartialEq, Eq, Hash)]
88
pub enum CoapOptionType {
89
    /// If-Match option ([RFC 7252, Section 5.10.8.1](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.8.1)).
90
    IfMatch = COAP_OPTION_IF_MATCH as u16,
91
    /// Uri-Host option ([RFC 7252, Section 5.10.1](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.1)).
92
    UriHost = COAP_OPTION_URI_HOST as u16,
93
    /// ETag option ([RFC 7252, Section 5.10.6](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.6)).
94
    ETag = COAP_OPTION_ETAG as u16,
95
    /// If-None-Match option ([RFC 7252, Section 5.10.8.2](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.8.2)).
96
    IfNoneMatch = COAP_OPTION_IF_NONE_MATCH as u16,
97
    /// Observe option ([RFC 7641, Section 2](https://datatracker.ietf.org/doc/html/rfc7641#section-2)).
98
    Observe = COAP_OPTION_OBSERVE as u16,
99
    /// Uri-Port option ([RFC 7252, Section 5.10.1](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.1)).
100
    UriPort = COAP_OPTION_URI_PORT as u16,
101
    /// Location-Path option ([RFC 7252, Section 5.10.7](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.7)).
102
    LocationPath = COAP_OPTION_LOCATION_PATH as u16,
103
    /// OSCORE option ([RFC 8613, Section 2](https://datatracker.ietf.org/doc/html/rfc8613#section-2).
104
    Oscore = COAP_OPTION_OSCORE as u16,
105
    /// Uri-Path option ([RFC 7252, Section 5.10.1](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.1)).
106
    UriPath = COAP_OPTION_URI_PATH as u16,
107
    /// Content-Format option ([RFC 7252, Section 5.10.3](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.3)).
108
    ContentFormat = COAP_OPTION_CONTENT_FORMAT as u16,
109
    /// Max-Age option ([RFC 7252, Section 5.10.5](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.5)).
110
    MaxAge = COAP_OPTION_MAXAGE as u16,
111
    /// Uri-Query option ([RFC 7252, Section 5.10.1](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.1)).
112
    UriQuery = COAP_OPTION_URI_QUERY as u16,
113
    /// Hop-Limit option ([RFC 8768, Section 3](https://datatracker.ietf.org/doc/html/rfc8768#section-3)).
114
    HopLimit = COAP_OPTION_HOP_LIMIT as u16,
115
    /// Accept option ([RFC 7252, Section 5.10.4](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.4)).
116
    Accept = COAP_OPTION_ACCEPT as u16,
117
    /// Q-Block1 option ([RFC 9177, Section 4](https://datatracker.ietf.org/doc/html/rfc9177#section-4)).
118
    QBlock1 = COAP_OPTION_Q_BLOCK1 as u16,
119
    /// Location-Query option ([RFC 7252, Section 5.10.7](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.7)).
120
    LocationQuery = COAP_OPTION_LOCATION_QUERY as u16,
121
    /// Block2 option ([RFC 7959, Section 2.1](https://datatracker.ietf.org/doc/html/rfc7959#section-2.1)).
122
    Block2 = COAP_OPTION_BLOCK2 as u16,
123
    /// Block1 option ([RFC 7959, Section 2.1](https://datatracker.ietf.org/doc/html/rfc7959#section-2.1)).
124
    Block1 = COAP_OPTION_BLOCK1 as u16,
125
    /// Size2 option ([RFC 7959, Section 4](https://datatracker.ietf.org/doc/html/rfc7959#section-4)).
126
    Size2 = COAP_OPTION_SIZE2 as u16,
127
    /// Q-Block2 option ([RFC 9177, Section 4](https://datatracker.ietf.org/doc/html/rfc9177#section-4)).
128
    QBlock2 = COAP_OPTION_Q_BLOCK2 as u16,
129
    /// Proxy-Uri option ([RFC 7252, Section 5.10.2](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.2)).
130
    ProxyUri = COAP_OPTION_PROXY_URI as u16,
131
    /// Proxy-Scheme option ([RFC 7252, Section 5.10.2](https://datatracker.ietf.org/doc/html/rfc7252#section-5.10.2)).
132
    ProxyScheme = COAP_OPTION_PROXY_SCHEME as u16,
133
    /// Size1 option ([RFC 7959, Section 4](https://datatracker.ietf.org/doc/html/rfc7959#section-4)).
134
    Size1 = COAP_OPTION_SIZE1 as u16,
135
    /// Echo option ([RFC 9175, Section 2.2](https://datatracker.ietf.org/doc/html/rfc9175#section-2.2)).
136
    Echo = COAP_OPTION_ECHO as u16,
137
    /// No-Response option ([RFC 7967, Section 2](https://datatracker.ietf.org/doc/html/rfc7967#section-2)).
138
    NoResponse = COAP_OPTION_NORESPONSE as u16,
139
    /// Request-Tag option ([RFC 9175, Section 3.2](https://datatracker.ietf.org/doc/html/rfc9175#section-3.2)).
140
    RTag = COAP_OPTION_RTAG as u16,
141
}
142

            
143
impl CoapOptionType {
144
    /// Returns the option number this type belongs to.
145
    pub fn to_raw_option_num(self) -> coap_option_num_t {
146
        self as u16
147
    }
148

            
149
    /// Returns the maximum size in bytes that a value of this option type should have.
150
932
    pub fn max_len(&self) -> usize {
151
932
        match self {
152
            CoapOptionType::IfMatch => 8,
153
            CoapOptionType::UriHost => 255,
154
            CoapOptionType::ETag => 8,
155
            CoapOptionType::IfNoneMatch => 0,
156
            CoapOptionType::UriPort => 2,
157
            CoapOptionType::LocationPath => 255,
158
699
            CoapOptionType::UriPath => 255,
159
            CoapOptionType::ContentFormat => 2,
160
            CoapOptionType::MaxAge => 4,
161
            CoapOptionType::UriQuery => 255,
162
            CoapOptionType::Accept => 2,
163
            CoapOptionType::LocationQuery => 255,
164
            CoapOptionType::ProxyUri => 1034,
165
            CoapOptionType::ProxyScheme => 255,
166
            CoapOptionType::Size1 => 4,
167
            CoapOptionType::Size2 => 4,
168
            CoapOptionType::Block1 => 3,
169
            CoapOptionType::Block2 => 3,
170
            CoapOptionType::HopLimit => 1,
171
            CoapOptionType::NoResponse => 1,
172
            CoapOptionType::Observe => 3,
173
            CoapOptionType::Oscore => 255,
174
            CoapOptionType::Echo => 40,
175
233
            CoapOptionType::RTag => 8,
176
            CoapOptionType::QBlock1 => 3,
177
            CoapOptionType::QBlock2 => 3,
178
        }
179
932
    }
180

            
181
    /// Returns the minimum size in bytes that a value of this option type should have.
182
932
    pub fn min_len(&self) -> usize {
183
932
        match self {
184
            CoapOptionType::IfMatch => 0,
185
            CoapOptionType::UriHost => 1,
186
            CoapOptionType::ETag => 1,
187
            CoapOptionType::IfNoneMatch => 0,
188
            CoapOptionType::UriPort => 0,
189
            CoapOptionType::LocationPath => 0,
190
699
            CoapOptionType::UriPath => 0,
191
            CoapOptionType::ContentFormat => 0,
192
            CoapOptionType::MaxAge => 0,
193
            CoapOptionType::UriQuery => 0,
194
            CoapOptionType::Accept => 0,
195
            CoapOptionType::LocationQuery => 0,
196
            CoapOptionType::ProxyUri => 1,
197
            CoapOptionType::ProxyScheme => 1,
198
            CoapOptionType::Size1 => 0,
199
            CoapOptionType::Size2 => 0,
200
            CoapOptionType::Block1 => 0,
201
            CoapOptionType::Block2 => 0,
202
            CoapOptionType::HopLimit => 1,
203
            CoapOptionType::NoResponse => 0,
204
            CoapOptionType::Observe => 0,
205
            CoapOptionType::Oscore => 0,
206
            CoapOptionType::Echo => 1,
207
233
            CoapOptionType::RTag => 0,
208
            CoapOptionType::QBlock1 => 0,
209
            CoapOptionType::QBlock2 => 0,
210
        }
211
932
    }
212
}
213

            
214
impl TryFrom<coap_option_num_t> for CoapOptionType {
215
    type Error = UnknownOptionError;
216

            
217
699
    fn try_from(num: coap_option_num_t) -> Result<Self, Self::Error> {
218
699
        <CoapOptionType as FromPrimitive>::from_u16(num).ok_or(UnknownOptionError::Unknown)
219
699
    }
220
}
221

            
222
/// Various content formats that can be used for CoAP requests.
223
///
224
/// To get the corresponding numeric value, use `[value] as u16`.
225
///
226
/// See <https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#content-formats> for
227
/// values that are currently registered with the IANA.
228
#[repr(u16)]
229
#[derive(Copy, Clone, FromPrimitive, Eq, PartialEq, Hash, Debug)]
230
#[non_exhaustive]
231
pub enum CoapContentFormat {
232
    Cbor = COAP_MEDIATYPE_APPLICATION_CBOR as u16,
233
    DotsCbor = COAP_MEDIATYPE_APPLICATION_DOTS_CBOR as u16,
234
    SenMlCbor = COAP_MEDIATYPE_APPLICATION_SENML_CBOR as u16,
235
    SenMlExi = COAP_MEDIATYPE_APPLICATION_SENML_EXI as u16,
236
    CoseEncrypt = COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT as u16,
237
    CoseEncrypt0 = COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT0 as u16,
238
    CoseKey = COAP_MEDIATYPE_APPLICATION_COSE_KEY as u16,
239
    CoseKeySet = COAP_MEDIATYPE_APPLICATION_COSE_KEY_SET as u16,
240
    CoseMac = COAP_MEDIATYPE_APPLICATION_COSE_MAC as u16,
241
    CoseMac0 = COAP_MEDIATYPE_APPLICATION_COSE_MAC0 as u16,
242
    CoseSign = COAP_MEDIATYPE_APPLICATION_COSE_SIGN as u16,
243
    CoseSign1 = COAP_MEDIATYPE_APPLICATION_COSE_SIGN1 as u16,
244
    Cwt = COAP_MEDIATYPE_APPLICATION_CWT as u16,
245
    Exi = COAP_MEDIATYPE_APPLICATION_EXI as u16,
246
    Json = COAP_MEDIATYPE_APPLICATION_JSON as u16,
247
    LinkFormat = COAP_MEDIATYPE_APPLICATION_LINK_FORMAT as u16,
248
    OctetStream = COAP_MEDIATYPE_APPLICATION_OCTET_STREAM as u16,
249
    RdfXml = COAP_MEDIATYPE_APPLICATION_RDF_XML as u16,
250
    SenMlJson = COAP_MEDIATYPE_APPLICATION_SENML_JSON as u16,
251
    SenMlXml = COAP_MEDIATYPE_APPLICATION_SENML_XML as u16,
252
    SensMlCbor = COAP_MEDIATYPE_APPLICATION_SENSML_CBOR as u16,
253
    SensMlExi = COAP_MEDIATYPE_APPLICATION_SENSML_EXI as u16,
254
    SensMlJson = COAP_MEDIATYPE_APPLICATION_SENSML_JSON as u16,
255
    SensMlXml = COAP_MEDIATYPE_APPLICATION_SENSML_XML as u16,
256
    ApplicationXml = COAP_MEDIATYPE_APPLICATION_XML as u16,
257
    TextPlain = COAP_MEDIATYPE_TEXT_PLAIN as u16,
258
    AceCbor = COAP_MEDIATYPE_APPLICATION_ACE_CBOR as u16,
259
    CoapGroupJson = COAP_MEDIATYPE_APPLICATION_COAP_GROUP_JSON as u16,
260
    MbCborSeq = COAP_MEDIATYPE_APPLICATION_MB_CBOR_SEQ as u16,
261
    Oscore = COAP_MEDIATYPE_APPLICATION_OSCORE as u16,
262
    Other,
263
}
264

            
265
impl From<ContentFormat> for CoapContentFormat {
266
    fn from(value: u16) -> Self {
267
        <CoapContentFormat as FromPrimitive>::from_u16(value).unwrap_or(CoapContentFormat::Other)
268
    }
269
}
270

            
271
/// Representation of a CoAP message code.
272
/// Can be a request code, a response code, or the empty message code.
273
///
274
/// The numeric value (that can also be obtained with [to_raw_request()](CoapRequestCode::to_raw_pdu_code()))
275
/// corresponds to the values defined in <https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#codes>.
276
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
277
#[repr(u8)]
278
pub enum CoapMessageCode {
279
    Empty,
280
    Request(CoapRequestCode),
281
    Response(CoapResponseCode),
282
}
283

            
284
impl CoapMessageCode {
285
    /// Returns the corresponding raw code for this message code, which can be added to a raw
286
    /// [coap_pdu_t](libcoap_sys::coap_pdu_t).
287
932
    pub fn to_raw_pdu_code(self) -> coap_pdu_code_t {
288
932
        match self {
289
            CoapMessageCode::Empty => coap_pdu_code_t::COAP_EMPTY_CODE,
290
466
            CoapMessageCode::Request(req) => req.to_raw_pdu_code(),
291
466
            CoapMessageCode::Response(rsp) => rsp.to_raw_pdu_code(),
292
        }
293
932
    }
294
}
295

            
296
impl From<CoapRequestCode> for CoapMessageCode {
297
233
    fn from(code: CoapRequestCode) -> Self {
298
233
        CoapMessageCode::Request(code)
299
233
    }
300
}
301

            
302
impl From<CoapResponseCode> for CoapMessageCode {
303
    fn from(code: CoapResponseCode) -> Self {
304
        CoapMessageCode::Response(code)
305
    }
306
}
307

            
308
impl TryFrom<coap_pdu_code_t> for CoapMessageCode {
309
    type Error = MessageCodeError;
310

            
311
699
    fn try_from(code: coap_pdu_code_t) -> Result<Self, Self::Error> {
312
699
        match code {
313
233
            coap_pdu_code_t::COAP_EMPTY_CODE => Ok(CoapMessageCode::Empty),
314
466
            code => CoapRequestCode::try_from(code)
315
466
                .map(CoapMessageCode::Request)
316
466
                .or_else(|_| CoapResponseCode::try_from(code).map(CoapMessageCode::Response)),
317
        }
318
699
    }
319
}
320

            
321
/// Representation of a CoAP request/method code.
322
///
323
/// See <https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#method-codes> for the
324
/// values currently registered with the IANA.
325
#[repr(u8)]
326
#[non_exhaustive]
327
466
#[derive(FromPrimitive, Clone, Copy, Eq, PartialEq, Hash, Debug)]
328
pub enum CoapRequestCode {
329
    Get = coap_pdu_code_t::COAP_REQUEST_CODE_GET as u8,
330
    Put = coap_pdu_code_t::COAP_REQUEST_CODE_PUT as u8,
331
    Delete = coap_pdu_code_t::COAP_REQUEST_CODE_DELETE as u8,
332
    Post = coap_pdu_code_t::COAP_REQUEST_CODE_POST as u8,
333
    Fetch = coap_pdu_code_t::COAP_REQUEST_CODE_FETCH as u8,
334
    IPatch = coap_pdu_code_t::COAP_REQUEST_CODE_IPATCH as u8,
335
    Patch = coap_pdu_code_t::COAP_REQUEST_CODE_PATCH as u8,
336
}
337

            
338
impl CoapRequestCode {
339
    /// Returns the [coap_request_t](coap_request_t) corresponding to this request code.
340
    ///
341
    /// Note that this is *not* the code that should be set inside of a [coap_pdu_t](libcoap_sys::coap_pdu_t),
342
    /// but a value used internally by the libcoap C library. See [to_raw_pdu_code()](CoapRequestCode::to_raw_pdu_code())
343
    /// for the standardized value used in messages.
344
233
    pub fn to_raw_request(self) -> coap_request_t {
345
233
        match self {
346
233
            CoapRequestCode::Get => coap_request_t::COAP_REQUEST_GET,
347
            CoapRequestCode::Put => coap_request_t::COAP_REQUEST_PUT,
348
            CoapRequestCode::Delete => coap_request_t::COAP_REQUEST_FETCH,
349
            CoapRequestCode::Post => coap_request_t::COAP_REQUEST_POST,
350
            CoapRequestCode::Fetch => coap_request_t::COAP_REQUEST_FETCH,
351
            CoapRequestCode::IPatch => coap_request_t::COAP_REQUEST_IPATCH,
352
            CoapRequestCode::Patch => coap_request_t::COAP_REQUEST_PATCH,
353
        }
354
233
    }
355

            
356
    /// Returns the raw [coap_pdu_code_t](coap_pdu_code_t) corresponding to this
357
    /// request code.
358
466
    pub fn to_raw_pdu_code(self) -> coap_pdu_code_t {
359
466
        match self {
360
466
            CoapRequestCode::Get => coap_pdu_code_t::COAP_REQUEST_CODE_GET,
361
            CoapRequestCode::Put => coap_pdu_code_t::COAP_REQUEST_CODE_PUT,
362
            CoapRequestCode::Delete => coap_pdu_code_t::COAP_REQUEST_CODE_FETCH,
363
            CoapRequestCode::Post => coap_pdu_code_t::COAP_REQUEST_CODE_POST,
364
            CoapRequestCode::Fetch => coap_pdu_code_t::COAP_REQUEST_CODE_FETCH,
365
            CoapRequestCode::IPatch => coap_pdu_code_t::COAP_REQUEST_CODE_IPATCH,
366
            CoapRequestCode::Patch => coap_pdu_code_t::COAP_REQUEST_CODE_PATCH,
367
        }
368
466
    }
369
}
370

            
371
impl From<coap_request_t> for CoapRequestCode {
372
    fn from(req: coap_request_t) -> Self {
373
        match req {
374
            coap_request_t::COAP_REQUEST_GET => CoapRequestCode::Get,
375
            coap_request_t::COAP_REQUEST_POST => CoapRequestCode::Post,
376
            coap_request_t::COAP_REQUEST_PUT => CoapRequestCode::Put,
377
            coap_request_t::COAP_REQUEST_DELETE => CoapRequestCode::Delete,
378
            coap_request_t::COAP_REQUEST_FETCH => CoapRequestCode::Fetch,
379
            coap_request_t::COAP_REQUEST_PATCH => CoapRequestCode::Patch,
380
            coap_request_t::COAP_REQUEST_IPATCH => CoapRequestCode::IPatch,
381
            _ => panic!("unknown request type"),
382
        }
383
    }
384
}
385

            
386
impl TryFrom<coap_pdu_code_t> for CoapRequestCode {
387
    type Error = MessageCodeError;
388

            
389
466
    fn try_from(req: coap_pdu_code_t) -> Result<Self, Self::Error> {
390
466
        <CoapRequestCode as FromPrimitive>::from_u32(req as u32).ok_or(MessageCodeError::NotARequestCode)
391
466
    }
392
}
393

            
394
/// Representation of a CoAP response code.
395
///
396
/// See <https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#response-codes> for
397
/// the possible values currently registered with the IANA.
398
#[repr(u8)]
399
#[non_exhaustive]
400
233
#[derive(Clone, Copy, FromPrimitive, Debug, Eq, PartialEq, Hash)]
401
pub enum CoapResponseCode {
402
    Content = coap_pdu_code_t::COAP_RESPONSE_CODE_CONTENT as u8,
403
    BadGateway = coap_pdu_code_t::COAP_RESPONSE_CODE_BAD_GATEWAY as u8,
404
    Continue = coap_pdu_code_t::COAP_RESPONSE_CODE_CONTINUE as u8,
405
    Conflict = coap_pdu_code_t::COAP_RESPONSE_CODE_CONFLICT as u8,
406
    BadRequest = coap_pdu_code_t::COAP_RESPONSE_CODE_BAD_REQUEST as u8,
407
    BadOption = coap_pdu_code_t::COAP_RESPONSE_CODE_BAD_OPTION as u8,
408
    Changed = coap_pdu_code_t::COAP_RESPONSE_CODE_CHANGED as u8,
409
    Created = coap_pdu_code_t::COAP_RESPONSE_CODE_CREATED as u8,
410
    Deleted = coap_pdu_code_t::COAP_RESPONSE_CODE_DELETED as u8,
411
    Forbidden = coap_pdu_code_t::COAP_RESPONSE_CODE_FORBIDDEN as u8,
412
    GatewayTimeout = coap_pdu_code_t::COAP_RESPONSE_CODE_GATEWAY_TIMEOUT as u8,
413
    HopLimitReached = coap_pdu_code_t::COAP_RESPONSE_CODE_HOP_LIMIT_REACHED as u8,
414
    Incomplete = coap_pdu_code_t::COAP_RESPONSE_CODE_INCOMPLETE as u8,
415
    InternalError = coap_pdu_code_t::COAP_RESPONSE_CODE_INTERNAL_ERROR as u8,
416
    NotAcceptable = coap_pdu_code_t::COAP_RESPONSE_CODE_NOT_ACCEPTABLE as u8,
417
    NotAllowed = coap_pdu_code_t::COAP_RESPONSE_CODE_NOT_ALLOWED as u8,
418
    NotFound = coap_pdu_code_t::COAP_RESPONSE_CODE_NOT_FOUND as u8,
419
    NotImplemented = coap_pdu_code_t::COAP_RESPONSE_CODE_NOT_IMPLEMENTED as u8,
420
    PreconditionFailed = coap_pdu_code_t::COAP_RESPONSE_CODE_PRECONDITION_FAILED as u8,
421
    ProxyingNotSupported = coap_pdu_code_t::COAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED as u8,
422
    RequestTooLarge = coap_pdu_code_t::COAP_RESPONSE_CODE_REQUEST_TOO_LARGE as u8,
423
    ServiceUnavailable = coap_pdu_code_t::COAP_RESPONSE_CODE_SERVICE_UNAVAILABLE as u8,
424
    TooManyRequests = coap_pdu_code_t::COAP_RESPONSE_CODE_TOO_MANY_REQUESTS as u8,
425
    Unauthorized = coap_pdu_code_t::COAP_RESPONSE_CODE_UNAUTHORIZED as u8,
426
    Unprocessable = coap_pdu_code_t::COAP_RESPONSE_CODE_UNPROCESSABLE as u8,
427
    UnsupportedContentFormat = coap_pdu_code_t::COAP_RESPONSE_CODE_UNSUPPORTED_CONTENT_FORMAT as u8,
428
    Valid = coap_pdu_code_t::COAP_RESPONSE_CODE_VALID as u8,
429
}
430

            
431
impl CoapResponseCode {
432
    /// Returns the raw [coap_pdu_code_t](coap_pdu_code_t) corresponding to this
433
    /// request code.
434
466
    pub fn to_raw_pdu_code(self) -> coap_pdu_code_t {
435
466
        match self {
436
466
            CoapResponseCode::Content => coap_pdu_code_t::COAP_RESPONSE_CODE_CONTENT,
437
            CoapResponseCode::BadGateway => coap_pdu_code_t::COAP_RESPONSE_CODE_BAD_GATEWAY,
438
            CoapResponseCode::Continue => coap_pdu_code_t::COAP_RESPONSE_CODE_CONTINUE,
439
            CoapResponseCode::Conflict => coap_pdu_code_t::COAP_RESPONSE_CODE_CONFLICT,
440
            CoapResponseCode::BadRequest => coap_pdu_code_t::COAP_RESPONSE_CODE_BAD_REQUEST,
441
            CoapResponseCode::BadOption => coap_pdu_code_t::COAP_RESPONSE_CODE_BAD_OPTION,
442
            CoapResponseCode::Changed => coap_pdu_code_t::COAP_RESPONSE_CODE_CHANGED,
443
            CoapResponseCode::Created => coap_pdu_code_t::COAP_RESPONSE_CODE_CREATED,
444
            CoapResponseCode::Deleted => coap_pdu_code_t::COAP_RESPONSE_CODE_DELETED,
445
            CoapResponseCode::Forbidden => coap_pdu_code_t::COAP_RESPONSE_CODE_FORBIDDEN,
446
            CoapResponseCode::GatewayTimeout => coap_pdu_code_t::COAP_RESPONSE_CODE_GATEWAY_TIMEOUT,
447
            CoapResponseCode::HopLimitReached => coap_pdu_code_t::COAP_RESPONSE_CODE_HOP_LIMIT_REACHED,
448
            CoapResponseCode::Incomplete => coap_pdu_code_t::COAP_RESPONSE_CODE_INCOMPLETE,
449
            CoapResponseCode::InternalError => coap_pdu_code_t::COAP_RESPONSE_CODE_INTERNAL_ERROR,
450
            CoapResponseCode::NotAcceptable => coap_pdu_code_t::COAP_RESPONSE_CODE_NOT_ACCEPTABLE,
451
            CoapResponseCode::NotAllowed => coap_pdu_code_t::COAP_RESPONSE_CODE_NOT_ALLOWED,
452
            CoapResponseCode::NotFound => coap_pdu_code_t::COAP_RESPONSE_CODE_NOT_FOUND,
453
            CoapResponseCode::NotImplemented => coap_pdu_code_t::COAP_RESPONSE_CODE_NOT_IMPLEMENTED,
454
            CoapResponseCode::PreconditionFailed => coap_pdu_code_t::COAP_RESPONSE_CODE_PRECONDITION_FAILED,
455
            CoapResponseCode::ProxyingNotSupported => coap_pdu_code_t::COAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED,
456
            CoapResponseCode::RequestTooLarge => coap_pdu_code_t::COAP_RESPONSE_CODE_REQUEST_TOO_LARGE,
457
            CoapResponseCode::ServiceUnavailable => coap_pdu_code_t::COAP_RESPONSE_CODE_SERVICE_UNAVAILABLE,
458
            CoapResponseCode::TooManyRequests => coap_pdu_code_t::COAP_RESPONSE_CODE_TOO_MANY_REQUESTS,
459
            CoapResponseCode::Unauthorized => coap_pdu_code_t::COAP_RESPONSE_CODE_UNAUTHORIZED,
460
            CoapResponseCode::Unprocessable => coap_pdu_code_t::COAP_RESPONSE_CODE_UNPROCESSABLE,
461
            CoapResponseCode::UnsupportedContentFormat => {
462
                coap_pdu_code_t::COAP_RESPONSE_CODE_UNSUPPORTED_CONTENT_FORMAT
463
            },
464
            CoapResponseCode::Valid => coap_pdu_code_t::COAP_RESPONSE_CODE_VALID,
465
        }
466
466
    }
467
}
468

            
469
impl Display for CoapResponseCode {
470
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
471
        let response_phrase = unsafe {
472
            let raw_phrase = coap_response_phrase(*self as u8);
473
            if raw_phrase.is_null() {
474
                "unknown response code"
475
            } else {
476
                CStr::from_ptr(raw_phrase)
477
                    .to_str()
478
                    .unwrap_or("unable to retrieve phrase for response code")
479
            }
480
        };
481

            
482
        write!(f, "{}", response_phrase)
483
    }
484
}
485

            
486
impl TryFrom<coap_pdu_code_t> for CoapResponseCode {
487
    type Error = MessageCodeError;
488

            
489
233
    fn try_from(value: coap_pdu_code_t) -> Result<Self, Self::Error> {
490
233
        <CoapResponseCode as FromPrimitive>::from_u32(value as u32).ok_or(MessageCodeError::NotAResponseCode)
491
233
    }
492
}
493

            
494
/// CoAP message types as defined in [RFC 7252, Section 3](https://datatracker.ietf.org/doc/html/rfc7252#section-3)
495
/// and described in [RFC 7252, Section 4.2 and 4.3](https://datatracker.ietf.org/doc/html/rfc7252#section-4.2).
496
#[repr(u8)]
497
699
#[derive(Copy, Clone, Hash, Eq, PartialEq, FromPrimitive, Debug)]
498
pub enum CoapMessageType {
499
    /// Confirmable message, i.e. a message whose reception should be confirmed by the peer.
500
    Con = COAP_MESSAGE_CON as u8,
501
    /// Non-confirmable message, i.e. a message whose reception should not be confirmed by the peer.
502
    Non = COAP_MESSAGE_NON as u8,
503
    /// Acknowledgement for a previous message.
504
    Ack = COAP_MESSAGE_ACK as u8,
505
    /// Non-acknowledgement for a previous message.
506
    Rst = COAP_MESSAGE_RST as u8,
507
}
508

            
509
impl CoapMessageType {
510
    /// Returns the corresponding raw [coap_pdu_type_t](coap_pdu_type_t) instance for
511
    /// this message type.
512
932
    pub fn to_raw_pdu_type(&self) -> coap_pdu_type_t {
513
932
        match self {
514
536
            CoapMessageType::Con => COAP_MESSAGE_CON,
515
            CoapMessageType::Non => COAP_MESSAGE_NON,
516
396
            CoapMessageType::Ack => COAP_MESSAGE_ACK,
517
            CoapMessageType::Rst => COAP_MESSAGE_RST,
518
        }
519
932
    }
520
}
521

            
522
impl From<coap_pdu_type_t> for CoapMessageType {
523
699
    fn from(raw_type: coap_pdu_type_t) -> Self {
524
699
        FromPrimitive::from_u32(raw_type as u32).expect("unknown PDU type")
525
699
    }
526
}