1use core::fmt;
2
3use hashbrown::HashMap;
4use serde::{
5 Deserialize,
6 Serialize,
7 de::Visitor,
8};
9use serde_string_enum::{
10 DeserializeLabeledStringEnum,
11 SerializeLabeledStringEnum,
12};
13
14#[derive(
16 Debug,
17 Default,
18 Clone,
19 Copy,
20 PartialEq,
21 Eq,
22 PartialOrd,
23 Ord,
24 Hash,
25 SerializeLabeledStringEnum,
26 DeserializeLabeledStringEnum,
27)]
28pub enum Type {
29 #[string = "Normal"]
30 #[default]
31 Normal,
32 #[string = "Fighting"]
33 Fighting,
34 #[string = "Flying"]
35 Flying,
36 #[string = "Poison"]
37 Poison,
38 #[string = "Ground"]
39 Ground,
40 #[string = "Rock"]
41 Rock,
42 #[string = "Bug"]
43 Bug,
44 #[string = "Ghost"]
45 Ghost,
46 #[string = "Steel"]
47 Steel,
48 #[string = "Fire"]
49 Fire,
50 #[string = "Water"]
51 Water,
52 #[string = "Grass"]
53 Grass,
54 #[string = "Electric"]
55 Electric,
56 #[string = "Psychic"]
57 Psychic,
58 #[string = "Ice"]
59 Ice,
60 #[string = "Dragon"]
61 Dragon,
62 #[string = "Dark"]
63 Dark,
64 #[string = "Fairy"]
65 Fairy,
66 #[string = "None"]
67 None,
68 #[string = "Stellar"]
69 Stellar,
70}
71
72#[derive(Debug, Default, Clone, Copy, PartialEq)]
74pub enum TypeEffectiveness {
75 None,
77 Weak,
79 #[default]
81 Normal,
82 Strong,
84}
85
86impl From<f32> for TypeEffectiveness {
87 fn from(value: f32) -> Self {
88 if value < 0f32 || (value).abs() < f32::EPSILON {
89 Self::None
90 } else if value < 0.5 || (value - 0.5).abs() < f32::EPSILON {
91 Self::Weak
92 } else if value < 1f32 || (value - 1f32).abs() < f32::EPSILON {
93 Self::Normal
94 } else {
95 Self::Strong
96 }
97 }
98}
99
100impl Into<f32> for TypeEffectiveness {
101 fn into(self) -> f32 {
102 match self {
103 Self::None => 0f32,
104 Self::Weak => 0.5,
105 Self::Normal => 1f32,
106 Self::Strong => 2f32,
107 }
108 }
109}
110
111impl Serialize for TypeEffectiveness {
112 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
113 where
114 S: serde::Serializer,
115 {
116 match &self {
117 Self::None => serializer.serialize_u32(Into::<f32>::into(*self) as u32),
118 TypeEffectiveness::Weak => serializer.serialize_f32(Into::<f32>::into(*self)),
119 Self::Normal => serializer.serialize_u32(Into::<f32>::into(*self) as u32),
120 Self::Strong => serializer.serialize_u32(Into::<f32>::into(*self) as u32),
121 }
122 }
123}
124
125struct TypeEffectivenessVisitor;
126
127impl<'de> Visitor<'de> for TypeEffectivenessVisitor {
128 type Value = TypeEffectiveness;
129
130 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
131 formatter.write_str("one of the following values: 0, 0.5, 1, 2")
132 }
133
134 fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
135 where
136 E: serde::de::Error,
137 {
138 Ok(Self::Value::from(v as f32))
139 }
140
141 fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
142 where
143 E: serde::de::Error,
144 {
145 Ok(Self::Value::from(v as f32))
146 }
147
148 fn visit_f32<E>(self, v: f32) -> Result<Self::Value, E>
149 where
150 E: serde::de::Error,
151 {
152 Ok(Self::Value::from(v))
153 }
154
155 fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
156 where
157 E: serde::de::Error,
158 {
159 Ok(Self::Value::from(v as f32))
160 }
161}
162
163impl<'de> Deserialize<'de> for TypeEffectiveness {
164 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
165 where
166 D: serde::Deserializer<'de>,
167 {
168 deserializer.deserialize_f32(TypeEffectivenessVisitor)
169 }
170}
171
172pub type TypeTable<T> = HashMap<Type, HashMap<T, TypeEffectiveness>>;
176
177#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
182pub struct TypeChart {
183 pub types: TypeTable<Type>,
184}
185
186impl TypeChart {
187 pub fn new() -> Self {
188 Self {
189 types: TypeTable::new(),
190 }
191 }
192
193 pub fn from_filled(types: TypeTable<Type>) -> Self {
194 Self { types }
195 }
196}
197
198#[cfg(test)]
199mod type_test {
200 use crate::{
201 Type,
202 test_util::{
203 test_string_deserialization,
204 test_string_serialization,
205 },
206 };
207
208 #[test]
209 fn serializes_to_string() {
210 test_string_serialization(Type::Grass, "Grass");
211 test_string_serialization(Type::Fire, "Fire");
212 test_string_serialization(Type::Water, "Water");
213 }
214
215 #[test]
216 fn deserializes_lowercase() {
217 test_string_deserialization("normal", Type::Normal);
218 test_string_deserialization("dragon", Type::Dragon);
219 test_string_deserialization("ghost", Type::Ghost);
220 }
221}
222
223#[cfg(test)]
224mod type_effectiveness_test {
225 use hashbrown::HashMap;
226
227 use crate::{
228 Type,
229 TypeChart,
230 TypeEffectiveness,
231 TypeTable,
232 test_util::test_deserialization,
233 };
234
235 #[test]
236 fn serializes_to_number() {
237 test_deserialization("0", TypeEffectiveness::None);
238 test_deserialization("0.5", TypeEffectiveness::Weak);
239 test_deserialization("1", TypeEffectiveness::Normal);
240 test_deserialization("2", TypeEffectiveness::Strong);
241 }
242
243 #[test]
244 fn deserializes_type_table() {
245 let str = r#"{
246 "Fire": {
247 "Fire": 0.5,
248 "Water": 0.5,
249 "Grass": 2,
250 "Ice": 2,
251 "Bug": 2,
252 "Rock": 0.5,
253 "Dragon": 0.5,
254 "Steel": 2
255 }
256 }"#;
257 let tc = serde_json::from_str::<TypeTable<Type>>(str).unwrap();
258 let expected = TypeTable::from_iter([(
259 Type::Fire,
260 HashMap::from_iter([
261 (Type::Fire, TypeEffectiveness::Weak),
262 (Type::Water, TypeEffectiveness::Weak),
263 (Type::Grass, TypeEffectiveness::Strong),
264 (Type::Ice, TypeEffectiveness::Strong),
265 (Type::Bug, TypeEffectiveness::Strong),
266 (Type::Rock, TypeEffectiveness::Weak),
267 (Type::Dragon, TypeEffectiveness::Weak),
268 (Type::Steel, TypeEffectiveness::Strong),
269 ]),
270 )]);
271 assert_eq!(tc, expected)
272 }
273
274 #[test]
275 fn deserializes_type_chart() {
276 let str = r#"{
277 "types": {
278 "Fire": {
279 "Fire": 0.5,
280 "Water": 0.5,
281 "Grass": 2,
282 "Ice": 2,
283 "Bug": 2,
284 "Rock": 0.5,
285 "Dragon": 0.5,
286 "Steel": 2
287 }
288 }
289 }"#;
290 let tc = serde_json::from_str::<TypeChart>(str).unwrap();
291 let expected = TypeChart::from_filled(TypeTable::from_iter([(
292 Type::Fire,
293 HashMap::from_iter([
294 (Type::Fire, TypeEffectiveness::Weak),
295 (Type::Water, TypeEffectiveness::Weak),
296 (Type::Grass, TypeEffectiveness::Strong),
297 (Type::Ice, TypeEffectiveness::Strong),
298 (Type::Bug, TypeEffectiveness::Strong),
299 (Type::Rock, TypeEffectiveness::Weak),
300 (Type::Dragon, TypeEffectiveness::Weak),
301 (Type::Steel, TypeEffectiveness::Strong),
302 ]),
303 )]));
304 assert_eq!(tc, expected)
305 }
306}