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}