mindus/
content.rs

1//! contains types of types
2use std::error::Error;
3
4macro_rules! numeric_enum {
5	($vis:vis enum $tname:ident for $numeric:ty | $error:ident {$($name:ident $(= $val:literal)?),* $(,)?}) =>
6	{
7		crate::content::numeric_enum!($vis enum $tname for $numeric | $error* {$($name $(= $val)?),*});
8
9		impl std::fmt::Display for $error {
10			fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
11				write!(f, "no variant of {} for value {}", stringify!($tname), self.0)
12			}
13		}
14
15		impl std::error::Error for $error {}
16	};
17	($vis:vis enum $tname:ident for $numeric:ty | $error:ident* {$($name:ident $(= $val:literal)?),* $(,)?}) =>
18	{
19		#[repr($numeric)]
20		#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
21		$vis enum $tname { $($name $(= $val)?,)+ }
22
23		#[derive(Copy, Clone, Debug, Eq, PartialEq)]
24		$vis struct $error($vis $numeric);
25
26		impl TryFrom<$numeric> for $tname {
27			type Error = $error;
28
29			#[allow(non_upper_case_globals)]
30			fn try_from(value: $numeric) -> Result<Self, $error> {
31				$(const $name: $numeric = $tname::$name as $numeric;)+
32				match value {
33					$($name => Ok(Self::$name),)+
34					_ => Err($error(value)),
35				}
36			}
37		}
38
39		impl From<$tname> for $numeric { fn from(value: $tname) -> $numeric { value as $numeric } }
40	};
41}
42
43pub(crate) use numeric_enum;
44
45macro_rules! count_exprs {
46    ($($e:expr)*) => {
47        $($crate::content::one!($e) +)* 0
48    }
49}
50
51macro_rules! one {
52    ($e:expr) => {
53        1
54    };
55}
56pub(crate) use count_exprs;
57pub(crate) use one;
58
59macro_rules! content_enum {
60	($vis:vis enum $tname:ident / $ctype:ident for u16 | $error:ident {$($val:literal),* $(,)?}) =>
61	{
62		paste::paste! {
63		$crate::content::numeric_enum!($vis enum $tname for u16 | $error* {
64			$([<$val:camel>]),*,
65		});
66
67		impl $tname {
68			pub const ALL: [Self; $crate::content::count_exprs!($($val)+)] = [$(Self::[<$val:camel>]),+];
69
70			pub (crate) fn by_name(name:&str)-> Option<Self> {
71				static MAPPER: std::sync::LazyLock<std::collections::HashMap<&str, $tname>> = std::sync::LazyLock::new(
72					|| std::collections::HashMap::from_iter(
73						[
74							$(($val, $tname::[<$val:camel>]),)+
75						]
76					)
77				);
78				MAPPER.get(name).copied()
79			}
80		}
81		impl const $crate::content::Content for $tname {
82			fn get_type(&self) -> $crate::content::Type {
83				$crate::content::Type::$ctype
84			}
85
86			fn get_id(&self) -> u16 {
87				*self as u16
88			}
89
90			fn get_name(&self) -> &'static str {
91				const NAME: [&str; {$tname::ALL.len()}] = [$($val,)*];
92				NAME[*self as usize]
93				// match self {
94				// 	$(Self::[<$val:camel>] => $val,)*
95				// }
96			}
97		}
98
99		impl std::fmt::Display for $error {
100			fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101				write!(f, "no content of type {} for value {}", stringify!($ctype), self.0)
102			}
103		}
104
105		impl std::fmt::Display for $tname {
106			fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107      	match self {
108					$(Self::[<$val:camel>] => f.write_str(strconv::kebab2title!($val)),)*
109				}
110			}
111	}
112
113		impl std::error::Error for $error {}
114	}
115	};
116}
117pub(crate) use content_enum;
118
119macro_rules! color_content_enum {
120	($vis:vis enum $tname:ident / $ctype:ident for u16 | $error:ident {$($val:literal: $col:literal),* $(,)?}) =>
121	{
122		paste::paste! {
123		$crate::content::content_enum!($vis enum $tname / $ctype for u16 | $error {
124			$($val),*,
125		});
126
127        impl Type {
128            #[must_use]
129            pub const fn color(&self) -> (u8, u8, u8) {
130                match &self {
131                    $(Self::[<$val:camel>] => {
132												let v = color_hex::color_from_hex!($col);
133												(v[0], v[1], v[2])
134                    },)*
135                }
136            }
137        }
138    }}
139}
140pub(crate) use color_content_enum;
141
142numeric_enum! {
143    pub enum Type for u8 | TryFromU8Error
144    {
145        Item = 0,
146        Block = 1,
147        // Mech = 2,
148        Bullet = 3,
149        Fluid = 4,
150        Modifier = 5,
151        Unit = 6,
152        Weather = 7,
153        // Effect = 8,
154        Sector = 9,
155        // Loadout = 10,
156        // TypeId = 11,
157        // Error = 12,
158        Planet = 13,
159        // Ammo = 14,
160        Team = 15,
161        UnitCommand = 16,
162        UnitStance = 17,
163    }
164}
165
166macro_rules! gen_by_id {
167    ($target:path, $id:expr) => {
168        match <$target>::try_from($id) {
169            Ok(v) => Ok(Box::new(v)),
170            Err(e) => Err(Box::new(e)),
171        }
172    };
173}
174
175impl Type {
176    pub fn get(self, id: u16) -> Result<Box<dyn Content>, Box<dyn Error>> {
177        match self {
178            Self::Item => gen_by_id!(crate::item::Type, id),
179            Self::Block => gen_by_id!(crate::block::content::Type, id),
180            Self::Fluid => gen_by_id!(crate::fluid::Type, id),
181            Self::Modifier => gen_by_id!(crate::modifier::Type, id),
182            Self::Unit => gen_by_id!(crate::unit::Type, id),
183            Self::Team => gen_by_id!(crate::team::Team, id),
184            _ => Ok(Box::new(Generic(self, id))),
185        }
186    }
187}
188#[const_trait]
189pub trait Content {
190    fn get_type(&self) -> Type;
191
192    fn get_id(&self) -> u16;
193
194    fn get_name(&self) -> &'static str;
195}
196
197struct Generic(Type, u16);
198
199impl Content for Generic {
200    fn get_type(&self) -> Type {
201        self.0
202    }
203
204    fn get_id(&self) -> u16 {
205        self.1
206    }
207
208    fn get_name(&self) -> &'static str {
209        "<unknown>"
210    }
211}