kwap_msg/
code.rs

1use kwap_macros::rfc_7252_doc;
2#[cfg(feature = "alloc")]
3use std_alloc::string::{String, ToString};
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 kwap_msg::Code;
16///
17/// assert_eq!(Code { class: 2,
18///                   detail: 5 }.to_string(),
19///            "2.05".to_string());
20/// ```
21#[derive(Copy, Clone, 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 kwap_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 `kwap` is enabled there is a `ToString` implementation provided for Code.
70  /// ```
71  /// use kwap_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 kwap_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(1, 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 {
103      | 0 => CodeKind::Empty,
104      | 1 => CodeKind::Request,
105      | _ => CodeKind::Response,
106    }
107  }
108}
109
110#[cfg(feature = "alloc")]
111#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
112impl ToString for Code {
113  fn to_string(&self) -> String {
114    String::from_iter(self.to_human())
115  }
116}
117
118impl From<u8> for Code {
119  fn from(b: u8) -> Self {
120    let class = b >> 5;
121    let detail = b & 0b0011111;
122
123    Code { class, detail }
124  }
125}
126
127impl From<Code> for u8 {
128  fn from(code: Code) -> u8 {
129    let class = code.class << 5;
130    let detail = code.detail;
131
132    class | detail
133  }
134}
135
136#[cfg(test)]
137mod tests {
138  use super::*;
139  use crate::assert_eqb;
140
141  #[test]
142  fn parse_code() {
143    let byte = 0b0100_0101_u8;
144    let code = Code::from(byte);
145    assert_eq!(code,
146               Code { class: 2,
147                      detail: 5 })
148  }
149
150  #[test]
151  fn serialize_code() {
152    let code = Code { class: 2,
153                      detail: 5 };
154    let actual: u8 = code.into();
155    let expected = 0b0100_0101_u8;
156    assert_eqb!(actual, expected)
157  }
158}