1use crate::as_transparent;
2use indexmap::IndexMap;
3use reikland::Ignored;
4use serde::{Deserialize, Deserializer, Serialize};
5
6#[derive(Debug, Clone, Deserialize, Serialize)]
7#[cfg_attr(test, serde(deny_unknown_fields))]
8pub struct Type {
9 #[serde(rename = "@id", deserialize_with = "as_transparent")]
10 pub id: String,
11 #[serde(rename = "@id_number")]
12 pub id_number: u8, #[serde(rename = "@real_name", deserialize_with = "as_transparent")]
14 pub real_name: String,
15 #[serde(rename = "@pseudo_type")]
16 pub pseudo_type: bool,
17 #[serde(rename = "@special_type")]
18 pub special_type: bool,
19 #[serde(rename = "@weaknesses")]
20 pub weaknesses: Vec<String>,
21 #[serde(rename = "@resistances")]
22 pub resistances: Vec<String>,
23 #[serde(rename = "@immunities")]
24 pub immunities: Vec<String>,
25}
26
27#[derive(Debug, Clone, Serialize)]
29pub struct TypeDex(pub Vec<Type>);
30
31impl<'de> serde::Deserialize<'de> for TypeDex {
32 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
33 where
34 D: Deserializer<'de>,
35 {
36 reikland::DualKeyVec::<(Type, Ignored)>::deserialize(deserializer)
37 .map(|reikland::DualKeyVec(mut d)| {
38 d.sort_by_key(|(t, _)| t.id_number);
39 d.into_iter().map(|(t, _)| t).collect()
40 })
41 .map(TypeDex)
42 }
43}
44
45#[derive(Debug, Clone, Serialize)]
47pub struct TypeMap(pub IndexMap<String, Type>);
48
49impl<'de> serde::Deserialize<'de> for TypeMap {
50 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
51 where
52 D: Deserializer<'de>,
53 {
54 TypeDex::deserialize(deserializer)
55 .map(|TypeDex(v)| TypeMap(v.into_iter().map(|t| (t.id.clone(), t)).collect()))
56 }
57}
58
59pub(crate) fn fuse_types<'a>(
60 head_1: &'a String,
61 head_2: Option<&'a String>,
62 body_1: &'a String,
63 body_2: Option<&'a String>,
64) -> (&'a String, Option<&'a String>) {
65 let type_1 = if let Some(secondary) = head_2
66 && is_normal_flying(head_1, head_2)
67 {
68 secondary
69 } else {
70 head_1
71 };
72
73 let type_2 = match body_2 {
74 None if body_1 == type_1 => None,
75 None => Some(body_1),
76 Some(secondary) if secondary == type_1 => Some(body_1),
77 Some(secondary) => Some(secondary),
78 };
79 (type_1, type_2)
80}
81
82fn is_normal_flying(head_1: &String, head_2: Option<&String>) -> bool {
83 if head_1 == "NORMAL" {
84 head_2.is_some_and(|v| v == "FLYING")
85 } else {
86 false
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use crate::test::PathTracked;
93
94 use super::*;
95
96 const TYPES_DAT: &[u8] = include_bytes!("../InfiniteFusion/Data/types.dat");
97
98 #[test]
99 fn deserialize_types_dat_vec() {
100 let PathTracked(dex) = reikland::from_bytes::<PathTracked<TypeDex>>(TYPES_DAT)
101 .unwrap_or_else(|e| panic!("types.dat failed to deserialize: {e}"));
102
103 assert!(!dex.0.is_empty());
104 assert_normal(dex.0.first().unwrap());
105 }
106
107 #[test]
108 fn deserialize_types_dat_map() {
109 let PathTracked(dex) = reikland::from_bytes::<PathTracked<TypeMap>>(TYPES_DAT)
110 .unwrap_or_else(|e| panic!("types.dat failed to deserialize: {e}"));
111
112 assert!(!dex.0.is_empty());
113 assert_normal(dex.0.first().unwrap().1);
114 }
115
116 fn assert_normal(t: &Type) {
117 assert_eq!(t.id, "NORMAL");
118 assert_eq!(t.id_number, 0);
119 assert_eq!(t.real_name, "Normal");
120 assert!(!t.pseudo_type);
121 assert!(!t.special_type);
122 assert_eq!(t.weaknesses, vec!["FIGHTING"]);
123 assert!(t.resistances.is_empty());
124 assert_eq!(t.immunities, vec!["GHOST"]);
125 }
126}