use microdot::{
helpers::{Header, SerializeStructHelper, HEADER_SIZE},
Deserialize, MicrodotError, Serialize,
};
pub trait IntoResponse<T> {
fn into_response(self) -> Response<T>;
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub struct Response<T> {
pub success: bool,
pub payload: Option<T>,
pub error: Option<Error>,
}
impl<T> Response<T> {
pub fn success() -> Self {
Self {
success: true,
payload: None,
error: None,
}
}
pub fn fail() -> Self {
Self {
success: false,
payload: None,
error: None,
}
}
pub fn with_payload(mut self, payload: T) -> Self {
self.payload = Some(payload);
self
}
pub fn with_err(mut self, err: Error) -> Self {
self.error = Some(err);
self
}
}
impl<T> Serialize for Response<T>
where
T: Serialize,
{
fn serialize(&self, buf: &mut [u8]) -> Result<usize, MicrodotError> {
let (header_buf, payload_buf) = buf.split_at_mut(HEADER_SIZE);
let mut helper = SerializeStructHelper::new(payload_buf);
let mut bytes_written = 0;
bytes_written += helper.serialize_field(&self.success)?;
if let Some(payload) = &self.payload {
bytes_written += helper.serialize_field(payload)?;
}
if let Some(error) = &self.error {
bytes_written += helper.serialize_field(error)?;
}
let header = Header {
id: 1,
size: bytes_written,
};
bytes_written += header.serialize(header_buf)?;
Ok(bytes_written)
}
}
impl<'a, T> Deserialize<'a> for Response<T>
where
T: Deserialize<'a>,
{
fn deserialize(buf: &'a [u8]) -> Result<Self, MicrodotError> {
let (header_buf, payload_buf) = buf.split_at(HEADER_SIZE);
let header = Header::deserialize(header_buf)?;
let buf = &payload_buf[..header.size];
let mut offset = 0;
let mut success = None;
let mut payload = None;
let mut error = None;
while offset < buf.len() {
let header = Header::deserialize(&buf[offset..offset + HEADER_SIZE])?;
let buf = &buf[offset + HEADER_SIZE..offset + HEADER_SIZE + header.size];
match header.id {
1 => {
success = Some(bool::deserialize(buf)?);
}
2 => {
payload = Some(<T>::deserialize(buf)?);
}
3 => {
error = Some(Error::deserialize(buf)?);
}
_ => return Err(MicrodotError::invalid_payload()),
}
offset += HEADER_SIZE + header.size;
}
let Some(success) = success else {
return Err(MicrodotError::missing_field("success"));
};
Ok(Self {
success,
payload,
error,
})
}
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum Error {
InvalidRequest,
ServerError,
}
impl Serialize for Error {
fn serialize(&self, buf: &mut [u8]) -> Result<usize, MicrodotError> {
if buf.is_empty() {
return Err(MicrodotError::invalid_length());
}
let byte = match self {
Error::InvalidRequest => 0x1,
Error::ServerError => 0x2,
};
buf[0] = byte;
Ok(1)
}
}
impl<'a> Deserialize<'a> for Error {
fn deserialize(buf: &'a [u8]) -> Result<Self, MicrodotError> {
if buf.is_empty() {
return Err(MicrodotError::invalid_length());
}
Ok(match buf[0] {
0x1 => Error::InvalidRequest,
0x2 => Error::ServerError,
_ => return Err(MicrodotError::invalid_payload()),
})
}
}
#[cfg(test)]
mod tests {
use crate::{
message::{check_update::CheckUpdateResponse, Message},
types::Version,
};
use super::*;
#[test]
fn response_should_serialize() {
let mut buf = [0u8; 32];
let bytes_written = Response::success()
.with_payload(Message::CheckUpdateResponse(CheckUpdateResponse {
next_version: Version::new(1, 2, 3),
}))
.serialize(&mut buf)
.expect("should be able to serialize known struct");
let response = Response::<Message>::deserialize(&buf[..bytes_written])
.expect("should be able to deserialize known struct");
match (&response.success, &response.payload, &response.error) {
(true, Some(payload), None) => {
if let Message::CheckUpdateResponse(res) = payload {
assert_eq!(res.next_version, Version::new(1, 2, 3,));
} else {
unreachable!()
}
}
_ => {
dbg!(&response);
dbg!(&buf[..]);
unreachable!()
}
}
}
}