nimbi_protocol/types/
response.rs1use microdot::{
2 helpers::{Header, SerializeStructHelper, HEADER_SIZE},
3 Deserialize, MicrodotError, Serialize,
4};
5
6pub trait IntoResponse<T> {
7 fn into_response(self) -> Response<T>;
8}
9
10#[cfg_attr(feature = "defmt", derive(defmt::Format))]
11#[derive(Debug)]
12pub struct Response<T> {
13 pub success: bool,
14 pub payload: Option<T>,
15 pub error: Option<Error>,
16}
17
18impl<T> Response<T> {
19 pub fn success() -> Self {
20 Self {
21 success: true,
22 payload: None,
23 error: None,
24 }
25 }
26
27 pub fn fail() -> Self {
28 Self {
29 success: false,
30 payload: None,
31 error: None,
32 }
33 }
34
35 pub fn with_payload(mut self, payload: T) -> Self {
36 self.payload = Some(payload);
37
38 self
39 }
40
41 pub fn with_err(mut self, err: Error) -> Self {
42 self.error = Some(err);
43
44 self
45 }
46}
47
48impl<T> Serialize for Response<T>
49where
50 T: Serialize,
51{
52 fn serialize(&self, buf: &mut [u8]) -> Result<usize, MicrodotError> {
53 let (header_buf, payload_buf) = buf.split_at_mut(HEADER_SIZE);
54
55 let mut helper = SerializeStructHelper::new(payload_buf);
56
57 let mut bytes_written = 0;
58
59 bytes_written += helper.serialize_field(&self.success)?;
60
61 if let Some(payload) = &self.payload {
62 bytes_written += helper.serialize_field(payload)?;
63 }
64
65 if let Some(error) = &self.error {
66 bytes_written += helper.serialize_field(error)?;
67 }
68
69 let header = Header {
70 id: 1,
71 size: bytes_written,
72 };
73
74 bytes_written += header.serialize(header_buf)?;
75
76 Ok(bytes_written)
77 }
78}
79
80impl<'a, T> Deserialize<'a> for Response<T>
81where
82 T: Deserialize<'a>,
83{
84 fn deserialize(buf: &'a [u8]) -> Result<Self, MicrodotError> {
85 let (header_buf, payload_buf) = buf.split_at(HEADER_SIZE);
86
87 let header = Header::deserialize(header_buf)?;
88
89 let buf = &payload_buf[..header.size];
90
91 let mut offset = 0;
92
93 let mut success = None;
94 let mut payload = None;
95 let mut error = None;
96
97 while offset < buf.len() {
98 let header = Header::deserialize(&buf[offset..offset + HEADER_SIZE])?;
99 let buf = &buf[offset + HEADER_SIZE..offset + HEADER_SIZE + header.size];
100
101 match header.id {
102 1 => {
103 success = Some(bool::deserialize(buf)?);
104 }
105 2 => {
106 payload = Some(<T>::deserialize(buf)?);
107 }
108 3 => {
109 error = Some(Error::deserialize(buf)?);
110 }
111 _ => return Err(MicrodotError::invalid_payload()),
112 }
113
114 offset += HEADER_SIZE + header.size;
115 }
116
117 let Some(success) = success else {
118 return Err(MicrodotError::missing_field("success"));
119 };
120
121 Ok(Self {
122 success,
123 payload,
124 error,
125 })
126 }
127}
128
129#[cfg_attr(feature = "defmt", derive(defmt::Format))]
130#[derive(Debug)]
131pub enum Error {
132 InvalidRequest,
133 ServerError,
134}
135
136impl Serialize for Error {
137 fn serialize(&self, buf: &mut [u8]) -> Result<usize, MicrodotError> {
138 if buf.is_empty() {
139 return Err(MicrodotError::invalid_length());
140 }
141
142 let byte = match self {
143 Error::InvalidRequest => 0x1,
144 Error::ServerError => 0x2,
145 };
146
147 buf[0] = byte;
148
149 Ok(1)
150 }
151}
152
153impl<'a> Deserialize<'a> for Error {
154 fn deserialize(buf: &'a [u8]) -> Result<Self, MicrodotError> {
155 if buf.is_empty() {
156 return Err(MicrodotError::invalid_length());
157 }
158
159 Ok(match buf[0] {
160 0x1 => Error::InvalidRequest,
161 0x2 => Error::ServerError,
162 _ => return Err(MicrodotError::invalid_payload()),
163 })
164 }
165}
166
167#[cfg(test)]
168mod tests {
169
170 use crate::{
171 message::{check_update::CheckUpdateResponse, Message},
172 types::Version,
173 };
174
175 use super::*;
176
177 #[test]
178 fn response_should_serialize() {
179 let mut buf = [0u8; 32];
180
181 let bytes_written = Response::success()
182 .with_payload(Message::CheckUpdateResponse(CheckUpdateResponse {
183 next_version: Version::new(1, 2, 3),
184 }))
185 .serialize(&mut buf)
186 .expect("should be able to serialize known struct");
187
188 let response = Response::<Message>::deserialize(&buf[..bytes_written])
189 .expect("should be able to deserialize known struct");
190
191 match (&response.success, &response.payload, &response.error) {
192 (true, Some(payload), None) => {
193 if let Message::CheckUpdateResponse(res) = payload {
194 assert_eq!(res.next_version, Version::new(1, 2, 3,));
195 } else {
196 unreachable!()
197 }
198 }
199 _ => {
200 dbg!(&response);
201 dbg!(&buf[..]);
202
203 unreachable!()
204 }
205 }
206 }
207}