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);