toad_msg/msg/code.rs
1#[cfg(feature = "alloc")]
2use std_alloc::string::{String, ToString};
3use toad_macros::rfc_7252_doc;
4
5#[doc = rfc_7252_doc!("12.1")]
6/// <details><summary><b>RFC7252 Section 12.1.1 Method Codes</b></summary>
7#[doc = concat!("\n#", rfc_7252_doc!("12.1.1"))]
8/// </details>
9/// <details><summary><b>RFC7252 Section 12.1.2 Response Codes</b></summary>
10#[doc = concat!("\n#", rfc_7252_doc!("12.1.2"))]
11/// </details>
12///
13/// # Examples
14/// ```
15/// use toad_msg::Code;
16///
17/// assert_eq!(Code { class: 2,
18/// detail: 5 }.to_string(),
19/// "2.05".to_string());
20/// ```
21#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug)]
22pub struct Code {
23 /// The "class" of message codes identify it as a request or response, and provides the class of response status:
24 ///
25 /// |class|meaning|
26 /// |---|---|
27 /// |`0`|Message is a request|
28 /// |`2`|Message is a success response|
29 /// |`4`|Message is a client error response|
30 /// |`5`|Message is a server error response|
31 pub class: u8,
32
33 /// 2-digit integer (range `[0, 32)`) that provides granular information about the response status.
34 ///
35 /// Will always be `0` for requests.
36 pub detail: u8,
37}
38
39/// Whether a code is for a request, response, or empty message
40#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub enum CodeKind {
42 /// A request code (0.xx)
43 Request,
44 /// A response code ([2-5].xx)
45 Response,
46 /// EMPTY (0.00)
47 Empty,
48}
49
50impl Code {
51 /// Create a new Code
52 ///
53 /// ```
54 /// use toad_msg::Code;
55 ///
56 /// let content = Code::new(2, 05);
57 /// ```
58 pub const fn new(class: u8, detail: u8) -> Self {
59 Self { class, detail }
60 }
61
62 /// Get the human string representation of a message code
63 ///
64 /// # Returns
65 /// A `char` array
66 ///
67 /// This is to avoid unnecessary heap allocation,
68 /// you can create a `String` with `FromIterator::<String>::from_iter`,
69 /// or if the `alloc` feature of `toad` is enabled there is a `ToString` implementation provided for Code.
70 /// ```
71 /// use toad_msg::Code;
72 ///
73 /// let code = Code { class: 2,
74 /// detail: 5 };
75 /// let chars = code.to_human();
76 /// let string = String::from_iter(chars);
77 /// assert_eq!(string, "2.05".to_string());
78 /// ```
79 pub fn to_human(&self) -> [char; 4] {
80 let to_char = |d: u8| char::from_digit(d.into(), 10).unwrap();
81 [to_char(self.class),
82 '.',
83 to_char(self.detail / 10),
84 to_char(self.detail % 10)]
85 }
86
87 /// Get whether this code is for a request, response, or empty message
88 ///
89 /// ```
90 /// use toad_msg::{Code, CodeKind};
91 ///
92 /// let empty: Code = Code::new(0, 0);
93 /// assert_eq!(empty.kind(), CodeKind::Empty);
94 ///
95 /// let req = Code::new(0, 1); // GET
96 /// assert_eq!(req.kind(), CodeKind::Request);
97 ///
98 /// let resp = Code::new(2, 5); // OK CONTENT
99 /// assert_eq!(resp.kind(), CodeKind::Response);
100 /// ```
101 pub fn kind(&self) -> CodeKind {
102 match (self.class, self.detail) {
103 | (0, 0) => CodeKind::Empty,
104 | (0, _) => CodeKind::Request,
105 | _ => CodeKind::Response,
106 }
107 }
108
109 #[doc = rfc_7252_doc!("4.1")]
110 pub const EMPTY: Self = Self::new(0, 0);
111
112 #[doc = rfc_7252_doc!("5.8.1")]
113 pub const GET: Self = Self::new(0, 1);
114
115 #[doc = rfc_7252_doc!("5.8.2")]
116 pub const POST: Self = Self::new(0, 3);
117
118 #[doc = rfc_7252_doc!("5.8.3")]
119 pub const PUT: Self = Self::new(0, 2);
120
121 #[doc = rfc_7252_doc!("5.8.4")]
122 pub const DELETE: Self = Self::new(0, 4);
123}
124
125#[cfg(feature = "alloc")]
126#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
127impl ToString for Code {
128 fn to_string(&self) -> String {
129 String::from_iter(self.to_human())
130 }
131}
132
133impl From<u8> for Code {
134 fn from(b: u8) -> Self {
135 // xxxyyyyy
136
137 // xxx => class
138 let class = b >> 5;
139
140 // yyyyy => detail
141 let detail = b & 0b00011111;
142
143 Code { class, detail }
144 }
145}
146
147impl From<Code> for u8 {
148 fn from(code: Code) -> u8 {
149 let class = (code.class << 5) & 0b11100000;
150 let detail = code.detail & 0b00011111;
151
152 class | detail
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159 use crate::assert_eqb;
160
161 #[test]
162 fn parse_code() {
163 let byte = 0b01000101_u8;
164 let code = Code::from(byte);
165 assert_eq!(code,
166 Code { class: 2,
167 detail: 5 })
168 }
169
170 #[test]
171 fn serialize_code() {
172 let code = Code { class: 2,
173 detail: 5 };
174 let actual: u8 = code.into();
175 let expected = 0b01000101_u8;
176 assert_eqb!(actual, expected)
177 }
178}