use super::frame::{self, Frame, Opcode};
use bytes::{Buf, BufMut};
use houseflow_macros::decoder;
use std::convert::TryInto;
use std::mem::size_of;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum DecodeError {
#[error("Invalid JSON: `{0}`")]
InvalidJSON(#[from] serde_json::Error),
#[error("Frame has invalid field `{field}`")]
InvalidField { field: &'static str },
#[error("Invalid size, expected: `{received}`, received: `{received}`")]
InvalidSize { expected: usize, received: usize },
}
pub trait Decoder {
const MIN_SIZE: usize;
fn decode(buf: &mut impl Buf) -> Result<Self, DecodeError>
where
Self: Sized;
}
pub trait Encoder {
fn encode(&self, buf: &mut impl BufMut);
}
impl Decoder for Frame {
const MIN_SIZE: usize = size_of::<Opcode>();
#[decoder]
fn decode(buf: &mut impl Buf) -> Result<Self, DecodeError> {
use frame::*;
use Opcode::*;
let opcode: Opcode = buf
.get_u8()
.try_into()
.map_err(|_| DecodeError::InvalidField { field: "opcode" })?;
let frame: Self = match opcode {
NoOperation => no_operation::Frame::decode(buf)?.into(),
State => state::Frame::decode(buf)?.into(),
Query => query::Frame::decode(buf)?.into(),
Execute => execute::Frame::decode(buf)?.into(),
ExecuteResponse => execute_response::Frame::decode(buf)?.into(),
};
Ok(frame)
}
}
impl Encoder for Frame {
fn encode(&self, buf: &mut impl BufMut) {
use Frame::*;
buf.put_u8(self.opcode() as u8);
match self {
NoOperation(frame) => frame.encode(buf),
State(frame) => frame.encode(buf),
Query(frame) => frame.encode(buf),
Execute(frame) => frame.encode(buf),
ExecuteResponse(frame) => frame.encode(buf),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use bytes::BytesMut;
use rand::random;
fn test_frame_codec<F>(frame: F)
where
F: Decoder + Encoder + std::fmt::Debug + PartialEq + Eq + Into<Frame> + Clone,
{
let mut buf = BytesMut::new();
let full_frame: Frame = frame.clone().into();
full_frame.encode(&mut buf);
let frame_decoded = Frame::decode(&mut buf).expect("failed decoding");
assert_eq!(frame.into(), frame_decoded);
assert_eq!(buf.remaining(), 0);
for i in 1..512 {
let buf: Vec<u8> = (0..i).map(|_| random()).collect();
let buf: &[u8] = buf.as_ref();
let buf = BytesMut::from(buf);
let _ = F::decode(&mut buf.clone());
let _ = Frame::decode(&mut buf.clone());
}
}
#[test]
fn no_operation() {
let frame = frame::no_operation::Frame {};
test_frame_codec(frame)
}
#[test]
fn state() {
let state = r#"
{
"on": true,
"online": true,
"openPercent": 20
}
"#;
let frame = frame::state::Frame {
state: serde_json::from_str(state).unwrap(),
};
test_frame_codec(frame)
}
#[test]
fn state_check() {
let frame = frame::query::Frame {};
test_frame_codec(frame)
}
#[test]
fn execute() {
let params = r#"
{
"on": true,
"online": true,
"openPercent": 20
}
"#;
let frame = frame::execute::Frame {
id: random(),
command: random(),
params: serde_json::from_str(params).unwrap(),
};
test_frame_codec(frame)
}
#[test]
fn execute_response() {
let state = r#"
{
"on": true,
"online": true,
"openPercent": 20
}
"#;
let frame = frame::execute_response::Frame {
id: random(),
status: random(),
error: random(),
state: serde_json::from_str(state).unwrap(),
};
test_frame_codec(frame)
}
}