1pub mod abilities;
2pub mod encounters;
3pub mod items;
4pub mod moves;
5pub mod species;
6pub mod types;
7
8use reikland::Transparent;
9use reikland::deserializer::MarshalDeserializeError;
10use serde::{Deserialize, Deserializer};
11
12pub use abilities::{AbilityDex, AbilityMap};
13pub use encounters::{EncounterDex, EncounterMap};
14pub use items::{ItemDex, ItemMap};
15pub use moves::{MoveDex, MoveMap};
16pub use species::{SpeciesDex, SpeciesMap};
17pub use types::{TypeDex, TypeMap};
18
19use crate::{
20 species::{Species, base_stats::BaseStats},
21 types::fuse_types,
22};
23
24pub(crate) fn as_transparent<'de, D, T>(deserializer: D) -> Result<T, D::Error>
26where
27 D: Deserializer<'de>,
28 T: Deserialize<'de>,
29{
30 Transparent::<T>::deserialize(deserializer).map(|t| t.0)
31}
32
33#[derive(Debug)]
34pub enum FromDatError {
35 Io(std::io::Error),
36 Marshal(MarshalDeserializeError),
37}
38
39impl std::fmt::Display for FromDatError {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 match self {
42 Self::Io(e) => write!(f, "io error: {e}"),
43 Self::Marshal(e) => write!(f, "marshal error: {e}"),
44 }
45 }
46}
47
48impl std::error::Error for FromDatError {
49 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
50 match self {
51 Self::Io(e) => Some(e),
52 Self::Marshal(e) => Some(e),
53 }
54 }
55}
56
57impl From<std::io::Error> for FromDatError {
58 fn from(e: std::io::Error) -> Self {
59 Self::Io(e)
60 }
61}
62
63impl From<MarshalDeserializeError> for FromDatError {
64 fn from(e: MarshalDeserializeError) -> Self {
65 Self::Marshal(e)
66 }
67}
68
69macro_rules! impl_from_dat {
71 ($t:ty) => {
72 impl $t {
73 pub fn from_bytes(bytes: &[u8]) -> Result<Self, FromDatError> {
74 Ok(reikland::from_bytes(bytes)?)
75 }
76
77 pub fn from_file(file: &mut std::fs::File) -> Result<Self, FromDatError> {
78 let mut bytes = Vec::new();
79 std::io::Read::read_to_end(file, &mut bytes)?;
80 Self::from_bytes(&bytes)
81 }
82
83 pub fn from_path(path: impl AsRef<std::path::Path>) -> Result<Self, FromDatError> {
84 let bytes = std::fs::read(path)?;
85 Self::from_bytes(&bytes)
86 }
87 }
88 };
89}
90
91impl_from_dat!(AbilityDex);
92impl_from_dat!(AbilityMap);
93impl_from_dat!(EncounterDex);
94impl_from_dat!(EncounterMap);
95impl_from_dat!(ItemDex);
96impl_from_dat!(ItemMap);
97impl_from_dat!(MoveDex);
98impl_from_dat!(MoveMap);
99impl_from_dat!(SpeciesDex);
100impl_from_dat!(SpeciesMap);
101impl_from_dat!(TypeDex);
102impl_from_dat!(TypeMap);
103
104#[derive(Debug, Clone)]
106pub struct FusedPokemon {
107 pub head_id: u32,
108 pub body_id: u32,
109 pub head_name: String,
110 pub body_name: String,
111 pub type1: String,
112 pub type2: Option<String>,
113 pub abilities: Vec<String>,
114 pub hidden_abilities: Vec<String>,
115 pub moves: Vec<(u8, String)>,
116 pub egg_moves: Vec<String>,
117 pub tutor_moves: Vec<String>,
118 pub base_stats: BaseStats,
119}
120
121pub fn fuse(head: &Species, body: &Species) -> FusedPokemon {
124 use itertools::Itertools;
125 let (type1, type2) = fuse_types(
126 &head.type1,
127 head.type2.as_ref(),
128 &body.type1,
129 body.type2.as_ref(),
130 );
131
132 fn combine_string_vec(head: &[String], body: &[String]) -> Vec<String> {
133 head.iter().chain(body.iter()).unique().cloned().collect()
134 }
135
136 let abilities = combine_string_vec(&head.abilities, &body.abilities);
137 let hidden_abilities = combine_string_vec(&head.hidden_abilities, &body.hidden_abilities);
138
139 let mut moves = head
140 .moves
141 .iter()
142 .chain(body.moves.iter())
143 .cloned()
144 .collect::<Vec<_>>();
145
146 moves.sort_unstable_by_key(|(level, _)| *level);
147
148 let egg_moves = combine_string_vec(&head.egg_moves, &body.egg_moves);
149 let tutor_moves = combine_string_vec(&head.tutor_moves, &body.tutor_moves);
150
151 let base_stats = head.base_stats.fuse(&body.base_stats);
152
153 FusedPokemon {
154 head_id: head.id_number,
155 body_id: body.id_number,
156 head_name: head.real_name.clone(),
157 body_name: body.real_name.clone(),
158 type1: type1.clone(),
159 type2: type2.cloned(),
160 abilities,
161 hidden_abilities,
162 moves,
163 egg_moves,
164 tutor_moves,
165 base_stats,
166 }
167}
168
169#[cfg(test)]
170pub(crate) mod test {
171 use serde::Deserializer;
172
173 pub(crate) struct PathTracked<T>(pub(crate) T);
174
175 impl<'de, T> serde::Deserialize<'de> for PathTracked<T>
176 where
177 T: serde::Deserialize<'de>,
178 {
179 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
180 where
181 D: Deserializer<'de>,
182 {
183 match serde_path_to_error::deserialize::<D, T>(deserializer) {
184 Ok(value) => Ok(PathTracked(value)),
185 Err(err) => {
186 let path = err.path().to_string();
187 let inner = err.into_inner();
188 Err(serde::de::Error::custom(format!("at `{path}`: {inner}")))
189 }
190 }
191 }
192 }
193}