use crate::*;
pub(crate) trait Ext: Default {
fn encode(&self) -> Result<u32>;
fn decode(v: u32) -> Result<Self>;
}
pub(crate) trait AtomExt: Sized {
const KIND_EXT: FourCC;
type Ext: Ext;
fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<Self::Ext>;
fn decode_body_ext<B: Buf>(buf: &mut B, ext: Self::Ext) -> Result<Self>;
}
impl<T: AtomExt> Atom for T {
const KIND: FourCC = Self::KIND_EXT;
fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
let ext = Ext::decode(u32::decode(buf)?)?;
AtomExt::decode_body_ext(buf, ext)
}
fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
let start = buf.len();
0u32.encode(buf)?;
let ext = self.encode_body_ext(buf)?;
let header = ext.encode()?;
buf.set_slice(start, &header.to_be_bytes());
Ok(())
}
}
impl Ext for () {
fn encode(&self) -> Result<u32> {
Ok(0)
}
fn decode(_: u32) -> Result<()> {
Ok(())
}
}
macro_rules! ext {
(name: $name:ident, versions: [$($version:expr),*], flags: { $($flag:ident = $bit:expr,)* }) => {
paste::paste! {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum [<$name Version>] {
$(
[<V $version>] = $version,
)*
}
impl TryFrom<u8> for [<$name Version>] {
type Error = Error;
fn try_from(v: u8) -> Result<Self> {
match v {
$(
$version => Ok(Self::[<V $version>]),
)*
_ => Err(Error::UnknownVersion(v)),
}
}
}
impl Default for [<$name Version>] {
#[allow(unreachable_code)]
fn default() -> Self {
$(
return Self::[<V $version>];
)*
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub(crate) struct [<$name Ext>] {
pub version: [<$name Version>],
$(
pub $flag: bool,
)*
}
impl Ext for [<$name Ext>] {
fn encode(&self) -> Result<u32>{
Ok((self.version as u32) << 24 $(| (self.$flag as u32) << $bit)*)
}
fn decode(v: u32) -> Result<Self> {
Ok([<$name Ext>] {
version: [<$name Version>]::try_from((v >> 24) as u8)?,
$(
$flag: (v & (1 << $bit)) != 0,
)*
})
}
}
impl From<[<$name Version>]> for [<$name Ext>] {
fn from(version: [<$name Version>]) -> Self {
let mut ext = Self::default();
ext.version = version;
ext
}
}
}
};
}
pub(crate) use ext;