//! contains types of types use std::error::Error; macro_rules! numeric_enum { ($vis:vis enum $tname:ident for $numeric:ty | $error:ident {$($name:ident $(= $val:literal)?),* $(,)?}) => { crate::content::numeric_enum!($vis enum $tname for $numeric | $error* {$($name $(= $val)?),*}); impl std::fmt::Display for $error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "no variant of {} for value {}", stringify!($tname), self.0) } } impl std::error::Error for $error {} }; ($vis:vis enum $tname:ident for $numeric:ty | $error:ident* {$($name:ident $(= $val:literal)?),* $(,)?}) => { #[repr($numeric)] #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] $vis enum $tname { $($name $(= $val)?,)+ } #[derive(Copy, Clone, Debug, Eq, PartialEq)] $vis struct $error($vis $numeric); impl TryFrom<$numeric> for $tname { type Error = $error; #[allow(non_upper_case_globals)] fn try_from(value: $numeric) -> Result { $(const $name: $numeric = $tname::$name as $numeric;)+ match value { $($name => Ok(Self::$name),)+ _ => Err($error(value)), } } } impl From<$tname> for $numeric { fn from(value: $tname) -> $numeric { value as $numeric } } }; } pub(crate) use numeric_enum; macro_rules! content_enum { ($vis:vis enum $tname:ident / $ctype:ident for u16 | $error:ident {$($val:literal),* $(,)?}) => { paste::paste! { $crate::content::numeric_enum!($vis enum $tname for u16 | $error* { $([<$val:camel>]),*, }); impl $crate::content::Content for $tname { fn get_type(&self) -> $crate::content::Type { $crate::content::Type::$ctype } fn get_id(&self) -> u16 { *self as u16 } fn get_name(&self) -> &'static str { match self { $(Self::[<$val:camel>] => $val,)* } } } impl std::fmt::Display for $error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "no content of type {} for value {}", stringify!($ctype), self.0) } } impl std::fmt::Display for $tname { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { $(Self::[<$val:camel>] => f.write_str(strconv::kebab2title!($val)),)* } } } impl std::error::Error for $error {} } }; } pub(crate) use content_enum; macro_rules! color_content_enum { ($vis:vis enum $tname:ident / $ctype:ident for u16 | $error:ident {$($val:literal: $col:literal),* $(,)?}) => { paste::paste! { $crate::content::content_enum!($vis enum $tname / $ctype for u16 | $error { $($val),*, }); impl Type { #[must_use] pub const fn color(&self) -> (u8, u8, u8) { match &self { $(Self::[<$val:camel>] => { let v = color_hex::color_from_hex!($col); (v[0], v[1], v[2]) },)* } } } }} } pub(crate) use color_content_enum; numeric_enum! { pub enum Type for u8 | TryFromU8Error { Item = 0, Block = 1, // Mech = 2, Bullet = 3, Fluid = 4, Modifier = 5, Unit = 6, Weather = 7, // Effect = 8, Sector = 9, // Loadout = 10, // TypeId = 11, // Error = 12, Planet = 13, // Ammo = 14, Team = 15, } } macro_rules! gen_by_id { ($target:path, $id:expr) => { match <$target>::try_from($id) { Ok(v) => Ok(Box::new(v)), Err(e) => Err(Box::new(e)), } }; } impl Type { pub fn get(self, id: u16) -> Result, Box> { match self { Self::Item => gen_by_id!(crate::item::Type, id), Self::Block => gen_by_id!(crate::block::content::Type, id), Self::Fluid => gen_by_id!(crate::fluid::Type, id), Self::Modifier => gen_by_id!(crate::modifier::Type, id), Self::Unit => gen_by_id!(crate::unit::Type, id), Self::Team => gen_by_id!(crate::team::Team, id), _ => Ok(Box::new(Generic(self, id))), } } } pub trait Content { fn get_type(&self) -> Type; fn get_id(&self) -> u16; fn get_name(&self) -> &str; } struct Generic(Type, u16); impl Content for Generic { fn get_type(&self) -> Type { self.0 } fn get_id(&self) -> u16 { self.1 } fn get_name(&self) -> &str { "" } }