use binrw::{BinWrite, binread};
use bytes::Buf;
use crate::protocol::{Error, Exception, Function, bytes::Decode, r#struct::Writable};
#[derive(Copy, Clone, BinWrite)]
#[bw(big)]
pub struct Request<T: Writable> {
pub function_code: u8,
pub args: T,
}
impl<T: Writable> Request<T> {
pub const fn wrap<F: Function<Args = T>>(args: T) -> Self {
Self { function_code: F::CODE, args }
}
}
#[binread]
#[br(big)]
#[derive(Copy, Clone)]
pub enum Response<F: Function> {
Ok {
#[br(assert(function_code == F::CODE))]
function_code: u8,
output: F::Output,
},
Exception {
#[br(assert(function_code == (F::CODE | 0x80)))]
function_code: u8,
exception: Exception,
},
}
impl<F> Decode for Response<F>
where
F: Function,
F::Output: Decode,
{
fn decode_from(buf: &mut (impl Buf + ?Sized)) -> Result<Self, Error> {
match buf.try_get_u8()? {
function_code if function_code == F::CODE => {
Ok(Self::Ok { function_code, output: F::Output::decode_from(buf)? })
}
function_code if function_code == (F::CODE | 0x80) => {
Ok(Self::Exception { function_code, exception: Exception::decode_from(buf)? })
}
function_code => Err(Error::UnexpectedFunctionCode(function_code)),
}
}
}
impl<F: Function> Response<F> {
pub fn into_result(self) -> Result<F::Output, Error> {
match self {
Self::Ok { output, .. } => Ok(output),
Self::Exception { exception, .. } => Err(Error::Exception(exception)),
}
}
}