Skip to main content

nimbi_protocol/types/
response.rs

1use 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}