1use 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 }
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 Bullet = 3,
149 Fluid = 4,
150 Modifier = 5,
151 Unit = 6,
152 Weather = 7,
153 Sector = 9,
155 Planet = 13,
159 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}