pub mod abilities;
pub mod encounters;
pub mod items;
pub mod moves;
pub mod species;
pub mod types;
use reikland::Transparent;
use reikland::deserializer::MarshalDeserializeError;
use serde::{Deserialize, Deserializer};
pub use abilities::{AbilityDex, AbilityMap};
pub use encounters::{EncounterDex, EncounterMap};
pub use items::{ItemDex, ItemMap};
pub use moves::{MoveDex, MoveMap};
pub use species::{SpeciesDex, SpeciesMap};
pub use types::{TypeDex, TypeMap};
use crate::{
species::{Species, base_stats::BaseStats},
types::fuse_types,
};
pub(crate) fn as_transparent<'de, D, T>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de>,
{
Transparent::<T>::deserialize(deserializer).map(|t| t.0)
}
#[derive(Debug)]
pub enum FromDatError {
Io(std::io::Error),
Marshal(MarshalDeserializeError),
}
impl std::fmt::Display for FromDatError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Io(e) => write!(f, "io error: {e}"),
Self::Marshal(e) => write!(f, "marshal error: {e}"),
}
}
}
impl std::error::Error for FromDatError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Io(e) => Some(e),
Self::Marshal(e) => Some(e),
}
}
}
impl From<std::io::Error> for FromDatError {
fn from(e: std::io::Error) -> Self {
Self::Io(e)
}
}
impl From<MarshalDeserializeError> for FromDatError {
fn from(e: MarshalDeserializeError) -> Self {
Self::Marshal(e)
}
}
macro_rules! impl_from_dat {
($t:ty) => {
impl $t {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, FromDatError> {
Ok(reikland::from_bytes(bytes)?)
}
pub fn from_file(file: &mut std::fs::File) -> Result<Self, FromDatError> {
let mut bytes = Vec::new();
std::io::Read::read_to_end(file, &mut bytes)?;
Self::from_bytes(&bytes)
}
pub fn from_path(path: impl AsRef<std::path::Path>) -> Result<Self, FromDatError> {
let bytes = std::fs::read(path)?;
Self::from_bytes(&bytes)
}
}
};
}
impl_from_dat!(AbilityDex);
impl_from_dat!(AbilityMap);
impl_from_dat!(EncounterDex);
impl_from_dat!(EncounterMap);
impl_from_dat!(ItemDex);
impl_from_dat!(ItemMap);
impl_from_dat!(MoveDex);
impl_from_dat!(MoveMap);
impl_from_dat!(SpeciesDex);
impl_from_dat!(SpeciesMap);
impl_from_dat!(TypeDex);
impl_from_dat!(TypeMap);
#[derive(Debug, Clone)]
pub struct FusedPokemon {
pub head_id: u32,
pub body_id: u32,
pub head_name: String,
pub body_name: String,
pub type1: String,
pub type2: Option<String>,
pub abilities: Vec<String>,
pub hidden_abilities: Vec<String>,
pub moves: Vec<(u8, String)>,
pub egg_moves: Vec<String>,
pub tutor_moves: Vec<String>,
pub base_stats: BaseStats,
}
pub fn fuse(head: &Species, body: &Species) -> FusedPokemon {
use itertools::Itertools;
let (type1, type2) = fuse_types(
&head.type1,
head.type2.as_ref(),
&body.type1,
body.type2.as_ref(),
);
fn combine_string_vec(head: &[String], body: &[String]) -> Vec<String> {
head.iter().chain(body.iter()).unique().cloned().collect()
}
let abilities = combine_string_vec(&head.abilities, &body.abilities);
let hidden_abilities = combine_string_vec(&head.hidden_abilities, &body.hidden_abilities);
let mut moves = head
.moves
.iter()
.chain(body.moves.iter())
.cloned()
.collect::<Vec<_>>();
moves.sort_unstable_by_key(|(level, _)| *level);
let egg_moves = combine_string_vec(&head.egg_moves, &body.egg_moves);
let tutor_moves = combine_string_vec(&head.tutor_moves, &body.tutor_moves);
let base_stats = head.base_stats.fuse(&body.base_stats);
FusedPokemon {
head_id: head.id_number,
body_id: body.id_number,
head_name: head.real_name.clone(),
body_name: body.real_name.clone(),
type1: type1.clone(),
type2: type2.cloned(),
abilities,
hidden_abilities,
moves,
egg_moves,
tutor_moves,
base_stats,
}
}
#[cfg(test)]
pub(crate) mod test {
use serde::Deserializer;
pub(crate) struct PathTracked<T>(pub(crate) T);
impl<'de, T> serde::Deserialize<'de> for PathTracked<T>
where
T: serde::Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
match serde_path_to_error::deserialize::<D, T>(deserializer) {
Ok(value) => Ok(PathTracked(value)),
Err(err) => {
let path = err.path().to_string();
let inner = err.into_inner();
Err(serde::de::Error::custom(format!("at `{path}`: {inner}")))
}
}
}
}
}