use std::io::{self, Write, Read};
use crate::net::bundle::{BundleElementWriter, TopElementReader, BundleElement, BundleResult};
use crate::util::io::*;
use super::{ElementLength, ElementIdRange, Element, TopElement};
pub trait MethodCall: Sized {
fn count() -> u16;
fn index(&self) -> u16;
fn len(index: u16) -> ElementLength;
fn encode(&self, write: &mut impl Write) -> io::Result<()>;
fn decode(read: &mut impl Read, len: usize, index: u16) -> io::Result<Self>;
}
pub trait MethodCallExt: TopElement<Config = ()> {
const ID_RANGE: ElementIdRange;
}
pub struct MethodCallWrapper<M, P>
where
M: MethodCall,
P: MethodCallExt,
{
pub method: M,
pub ext: P,
}
impl<M, P> MethodCallWrapper<M, P>
where
M: MethodCall,
P: MethodCallExt,
{
pub const DEFAULT_LEN: ElementLength = ElementLength::Callback(|id| {
if let Some(exposed_id) = P::ID_RANGE.to_exposed_id_checked(M::count(), id) {
M::len(exposed_id)
} else {
ElementLength::Variable16
}
});
pub fn new(method: M, prefix: P) -> Self {
Self { method, ext: prefix }
}
pub fn write(self, mut writer: BundleElementWriter) {
let (
element_id,
sub_id
) = P::ID_RANGE.from_exposed_id(M::count(), self.method.index());
writer.write(element_id, self, &(0, sub_id));
}
pub fn read(reader: TopElementReader) -> BundleResult<BundleElement<Self>> {
let element_id = reader.id();
reader.read::<Self>(&(element_id, None))
}
}
impl<M, P> Element for MethodCallWrapper<M, P>
where
M: MethodCall,
P: MethodCallExt,
{
type Config = (u8, Option<u8>);
fn encode(&self, write: &mut impl Write, config: &Self::Config) -> io::Result<()> {
self.ext.encode(write, &())?;
if let Some(sub_id) = config.1 {
write.write_u8(sub_id)?;
}
self.method.encode(write)
}
fn decode(read: &mut impl Read, mut len: usize, config: &Self::Config) -> io::Result<Self> {
let mut prefix_read = IoCounter::new(&mut *read);
let prefix = P::decode(&mut prefix_read, len, &())?;
len -= prefix_read.count();
let mut sub_id_err = None;
let exposed_id = P::ID_RANGE.to_exposed_id(M::count(), config.0, || {
len -= 1;
match read.read_u8() {
Ok(n) => n,
Err(e) => {
sub_id_err = Some(e);
0 }
}
});
if let Some(e) = sub_id_err {
return Err(e);
}
M::decode(read, len, exposed_id).map(|method| Self {
method,
ext: prefix,
})
}
}
impl<M, P> TopElement for MethodCallWrapper<M, P>
where
M: MethodCall,
P: MethodCallExt,
{
const LEN: ElementLength = P::LEN;
}