use crate::{
property::{Kind, NoMeta},
traits::{PropertyExt, PropertyValueExt, ReadProperty, WriteProperty},
};
use ltk_io_ext::{ReaderExt, WriterExt};
macro_rules! impl_prim {
($name:tt, $rust:tt, [$($derive:tt),*], $method:ident $(::<$endian:ident>)?) => {
impl_prim!($name, $rust, [$( $derive ),*], $method $(::< $endian >)?, value);
};
($name:tt, $rust:tt, [$($derive:tt),*], $method:ident $(::<$endian:ident>)?, $($write_value:tt)*) => {
#[derive(Clone, Debug, PartialEq, Default, $($derive),*)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(bound = "for <'dee> M: serde::Serialize + serde::Deserialize<'dee>")
)]
pub struct $name<M = NoMeta>{
pub value: $rust,
pub meta: M
}
impl<M> $name<M> {
#[inline(always)]
#[must_use]
pub fn new(value: $rust) -> Self where M: Default {
Self::new_with_meta(value, M::default())
}
#[inline(always)]
#[must_use]
pub fn new_with_meta(value: $rust, meta: M) -> Self {
Self { value, meta }
}
#[inline(always)]
#[must_use]
pub fn with_meta<T>(self, meta: T) -> $name<T> {
$name { value: self.value, meta }
}
#[inline(always)]
#[must_use]
pub fn no_meta(self) -> $name<NoMeta> {
self.with_meta(NoMeta)
}
}
impl<M> PropertyExt for $name<M> {
fn size_no_header(&self) -> usize {
core::mem::size_of::<$rust>()
}
type Meta = M;
fn meta(&self) -> &Self::Meta {
&self.meta
}
fn meta_mut(&mut self) -> &mut Self::Meta {
&mut self.meta
}
}
impl<M> PropertyValueExt for $name<M> {
const KIND: Kind = Kind::$name;
}
impl<M: Default> ReadProperty for $name<M> {
fn from_reader<R: std::io::Read + ?Sized>(
reader: &mut R,
_legacy: bool,
) -> Result<Self, crate::Error> {
Ok(Self {
value: paste::paste!(reader.[<read_ $method>]::<$($endian,)*>()?),
meta: M::default()
})
}
}
impl<M> WriteProperty for $name<M> {
fn to_writer<W: std::io::Write + std::io::Seek + ?Sized>(
&self,
writer: &mut W,
_legacy: bool,
) -> Result<(), std::io::Error> {
paste::paste!(writer.[<write_ $method>]::<$($endian,)*>(self.$($write_value)*))
}
}
impl<S: Into<$rust>, M: Default> From<S> for $name<M> {
fn from(value: S) -> Self {
Self::new(value.into())
}
}
impl<M> AsRef<$rust> for $name<M> {
fn as_ref(&self) -> &$rust {
&self.value
}
}
impl<M> std::ops::Deref for $name<M> {
type Target = $rust;
fn deref(&self) -> &Self::Target {
&self.value
}
}
};
}
use byteorder::{ReadBytesExt, WriteBytesExt, LE};
use glam::{Mat4, Vec2, Vec3, Vec4};
use ltk_primitives::Color as ColorPrim;
impl_prim!(Bool, bool, [Eq, Hash], bool);
impl_prim!(BitBool, bool, [Eq, Hash], bool);
impl_prim!(I8, i8, [Eq, Hash], i8);
impl_prim!(U8, u8, [Eq, Hash], u8);
impl_prim!(I16, i16, [Eq, Hash], i16::<LE>);
impl_prim!(U16, u16, [Eq, Hash], u16::<LE>);
impl_prim!(I32, i32, [Eq, Hash], i32::<LE>);
impl_prim!(U32, u32, [Eq, Hash], u32::<LE>);
impl_prim!(I64, i64, [Eq, Hash], i64::<LE>);
impl_prim!(U64, u64, [Eq, Hash], u64::<LE>);
impl_prim!(F32, f32, [], f32::<LE>);
impl_prim!(Vector2, Vec2, [], vec2::<LE>);
impl_prim!(Vector3, Vec3, [], vec3::<LE>);
impl_prim!(Vector4, Vec4, [], vec4::<LE>);
impl_prim!(Matrix44, Mat4, [], mat4_row_major::<LE>);
type ColorU8 = ColorPrim<u8>;
impl_prim!(Color, ColorU8, [], color_u8, value.as_ref());
impl_prim!(Hash, u32, [Eq, Hash], u32::<LE>);
impl_prim!(WadChunkLink, u64, [Eq, Hash], u64::<LE>);
impl_prim!(ObjectLink, u32, [Eq, Hash], u32::<LE>);