coap_numbers/
code.rs

1//! Constants and functions for CoAP codes
2//!
3//! This contains codes for all ranges -- request, response and signalling.
4//!
5//! Codes are expressed as u8 values in accordance with their serialized form.
6
7/// Format a CoAP code in dotted notation
8///
9/// This prints the class number of a code, followed by a dot and the two-digit detail, into a
10/// formatter. That format is the common human-readable expression of CoAP codes, as it eases the
11/// comparison to the (dot-less) codes of HTTP.
12///
13/// It is typically used to easily implement the Display trait on types that contain a code:
14///
15/// ```
16/// # use coap_numbers::code::*;
17/// struct Code(u8);
18///
19/// impl core::fmt::Display for Code {
20///     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
21///         format_dotted(self.0, f)?;
22///         if let Some(name) = to_name(self.0) {
23///             write!(f, " {}", name)?;
24///         }
25///         Ok(())
26///     }
27/// }
28///
29/// let g = format!("{}", Code(GET));
30/// assert_eq!(g, "0.01 GET");
31/// ```
32///
33/// # More formatting
34///
35/// This crate just provides very minimal formatting helpers in this function and in
36/// [`to_dotted()`]. More elaborate and ergonomic tools are available through the
37/// [`coap_message_utils::ShowCodeExt`](https://docs.rs/coap-message-utils/latest/coap_message_utils/trait.ShowCodeExt.html)
38/// trait.
39///
40/// # Errors
41///
42/// Like the [formatting traits](https://doc.rust-lang.org/std/fmt/index.html#formatting-traits),
43/// this only errs if the unerlying formatter errs.
44pub fn format_dotted(code: u8, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
45    write!(f, "{}.{:02}", code >> 5, code & 0x1f)
46}
47
48/// Convert a CoAP code to dotted notation
49///
50/// This expresses the class number of a code, followed by a dot and the two-digit detail, into a
51/// String. That format is the common human-readable expression of CoAP codes, as it eases the
52/// comparison to the (dot-less) codes of HTTP.
53///
54/// Example:
55///
56/// ```
57/// # use coap_numbers::code::*;
58/// let g: String = to_dotted(GET);
59/// assert_eq!(g, "0.01");
60/// let d = to_dotted(PRECONDITION_FAILED);
61/// assert_eq!(d, "4.12");
62/// ```
63///
64/// This is equivalent in functionality to [`format_dotted`](fn.format_dotted.html), which may be
65/// used as a replacement in ``no_std`` settings.
66#[cfg(feature = "alloc")]
67pub fn to_dotted(code: u8) -> alloc::string::String {
68    // It's easier to copy this down from format_dotted than to fill a String
69    alloc::format!("{}.{:02}", code >> 5, code & 0x1f)
70}
71
72/// Classification of CoAP codes used in responses
73#[non_exhaustive]
74#[derive(Copy, Clone, Debug, PartialEq)]
75pub enum Class {
76    /// 2.xx Success codes
77    Success,
78    /// 4.xx Client Error codes
79    ClientError,
80    /// 5.xx Server Error codes
81    ServerError,
82}
83
84/// Classification of CoAP codes in any message
85#[non_exhaustive]
86#[derive(Copy, Clone, Debug, PartialEq)]
87pub enum Range {
88    /// The 0.00 Empty code
89    Empty,
90    /// A request code (0.01 to 0.31)
91    Request,
92    /// A response code (2.00 to 5.31 excluding 3.xx)
93    ///
94    /// 3.xx codes will be classified in here when they are assigned and get a name
95    Response(Class),
96    /// A signalling code (7.xx)
97    Signaling,
98    /// Any other range
99    ///
100    /// Try not to match for this classification, as codes that are recognized as Reserved in one
101    /// version of coap-numbers may move into a newly created class later on.
102    Reserved,
103}
104
105/// Find which range a code is in
106///
107/// ```
108/// # use coap_numbers::code::*;
109/// assert_eq!(classify(GET), Range::Request);
110/// assert_eq!(classify(INTERNAL_SERVER_ERROR), Range::Response(Class::ServerError));
111///
112/// # let code = CONTENT;
113/// match classify(code) {
114///     Range::Response(Class::Success) => println!("Processing response"),
115///     Range::Response(_) => println!("Some error, probably"),
116///     _ => println!("Protocol violation"),
117/// }
118/// ```
119#[inline]
120#[must_use]
121pub fn classify(code: u8) -> Range {
122    match code {
123        0 => Range::Empty,
124        0x01..=0x1f => Range::Request,
125        0x40..=0x5f => Range::Response(Class::Success),
126        0x80..=0x9f => Range::Response(Class::ClientError),
127        0xa0..=0xbf => Range::Response(Class::ServerError),
128        0xe0..=0xff => Range::Signaling,
129        _ => Range::Reserved,
130    }
131}
132
133macro_rules! code {
134    ( $class:tt . $detail:tt ) => {
135        ($class << 5) + $detail
136    };
137}
138
139// FIXME: Generate documentaton at least saying which class the identifier is, and group them
140// visually (or at least keep them sorted)
141macro_rules! codes {
142    ( $( $name:tt $constname:ident  $class:tt $detail:expr ) , * ) => { $(
143            #[doc=$name]
144            pub const $constname: u8 = code!($class.$detail);
145        )*
146
147        /// Find the name for a CoAP code, if any is known
148        ///
149        /// Returns the registered name for a code, or None if the code is not known.
150        ///
151        /// ```
152        /// # use coap_numbers::code::*;
153        /// assert_eq!(to_name(HOP_LIMIT_REACHED), Some("Hop Limit Reached"));
154        /// assert_eq!(to_name(0x31), None);
155        /// ```
156        #[must_use]
157        pub fn to_name(code: u8) -> Option<&'static str> {
158            match code {
159                $(
160                $constname => Some($name),
161                )*
162                _ => None
163            }
164        }
165    }
166}
167
168codes!(
169    // Not formally registered, but special by having its own single-item code range
170    "Empty" EMPTY 0 0,
171
172    "GET" GET 0 1,
173    "POST" POST 0 2,
174    "PUT" PUT 0 3,
175    "DELETE" DELETE 0 4,
176    "FETCH" FETCH 0 5,
177    "PATCH" PATCH 0 6,
178    "iPATCH" IPATCH 0 7,
179
180    "Created" CREATED 2 1,
181    "Deleted" DELETED 2 2,
182    "Valid" VALID 2 3,
183    "Changed" CHANGED 2 4,
184    "Content" CONTENT 2 5,
185    "Continue" CONTINUE 2 31,
186    "Bad Request" BAD_REQUEST 4 0,
187    "Unauthorized" UNAUTHORIZED 4 1,
188    "Bad Option" BAD_OPTION 4 2,
189    "Forbidden" FORBIDDEN 4 3,
190    "Not Found" NOT_FOUND 4 4,
191    "Method Not Allowed" METHOD_NOT_ALLOWED 4 5,
192    "Not Acceptable" NOT_ACCEPTABLE 4 6,
193    "Request Entity Incomplete" REQUEST_ENTITY_INCOMPLETE 4 8,
194    "Conflict" CONFLICT 4 9,
195    "Precondition Failed" PRECONDITION_FAILED 4 12,
196    "Request Entity Too Large" REQUEST_ENTITY_TOO_LARGE 4 13,
197    "Unsupported Content-Format" UNSUPPORTED_CONTENT_FORMAT 4 15,
198    "Unprocessable Entity" UNPROCESSABLE_ENTITY 4 22,
199    "Too Many Requests" TOO_MANY_REQUESTS 4 29,
200    "Internal Server Error" INTERNAL_SERVER_ERROR 5 0,
201    "Not Implemented" NOT_IMPLEMENTED 5 1,
202    "Bad Gateway" BAD_GATEWAY 5 2,
203    "Service Unavailable" SERVICE_UNAVAILABLE 5 3,
204    "Gateway Timeout" GATEWAY_TIMEOUT 5 4,
205    "Proxying Not Supported" PROXYING_NOT_SUPPORTED 5 5,
206    "Hop Limit Reached" HOP_LIMIT_REACHED 5 8,
207
208    "CSM" CSM 7 1,
209    "Ping" PING 7 2,
210    "Pong" PONG 7 3,
211    "Release" RELEASE 7 4,
212    "Abort" ABORT 7 5
213);