use crate::as_transparent;
use indexmap::IndexMap;
use reikland::Ignored;
use serde::{Deserialize, Deserializer, Serialize};
#[derive(Debug, Clone, Deserialize, Serialize)]
#[cfg_attr(test, serde(deny_unknown_fields))]
pub struct Type {
#[serde(rename = "@id", deserialize_with = "as_transparent")]
pub id: String,
#[serde(rename = "@id_number")]
pub id_number: u8, #[serde(rename = "@real_name", deserialize_with = "as_transparent")]
pub real_name: String,
#[serde(rename = "@pseudo_type")]
pub pseudo_type: bool,
#[serde(rename = "@special_type")]
pub special_type: bool,
#[serde(rename = "@weaknesses")]
pub weaknesses: Vec<String>,
#[serde(rename = "@resistances")]
pub resistances: Vec<String>,
#[serde(rename = "@immunities")]
pub immunities: Vec<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct TypeDex(pub Vec<Type>);
impl<'de> serde::Deserialize<'de> for TypeDex {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
reikland::DualKeyVec::<(Type, Ignored)>::deserialize(deserializer)
.map(|reikland::DualKeyVec(mut d)| {
d.sort_by_key(|(t, _)| t.id_number);
d.into_iter().map(|(t, _)| t).collect()
})
.map(TypeDex)
}
}
#[derive(Debug, Clone, Serialize)]
pub struct TypeMap(pub IndexMap<String, Type>);
impl<'de> serde::Deserialize<'de> for TypeMap {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
TypeDex::deserialize(deserializer)
.map(|TypeDex(v)| TypeMap(v.into_iter().map(|t| (t.id.clone(), t)).collect()))
}
}
pub(crate) fn fuse_types<'a>(
head_1: &'a String,
head_2: Option<&'a String>,
body_1: &'a String,
body_2: Option<&'a String>,
) -> (&'a String, Option<&'a String>) {
let type_1 = if let Some(secondary) = head_2
&& is_normal_flying(head_1, head_2)
{
secondary
} else {
head_1
};
let type_2 = match body_2 {
None if body_1 == type_1 => None,
None => Some(body_1),
Some(secondary) if secondary == type_1 => Some(body_1),
Some(secondary) => Some(secondary),
};
(type_1, type_2)
}
fn is_normal_flying(head_1: &String, head_2: Option<&String>) -> bool {
if head_1 == "NORMAL" {
head_2.is_some_and(|v| v == "FLYING")
} else {
false
}
}
#[cfg(test)]
mod tests {
use crate::test::PathTracked;
use super::*;
const TYPES_DAT: &[u8] = include_bytes!("../InfiniteFusion/Data/types.dat");
#[test]
fn deserialize_types_dat_vec() {
let PathTracked(dex) = reikland::from_bytes::<PathTracked<TypeDex>>(TYPES_DAT)
.unwrap_or_else(|e| panic!("types.dat failed to deserialize: {e}"));
assert!(!dex.0.is_empty());
assert_normal(dex.0.first().unwrap());
}
#[test]
fn deserialize_types_dat_map() {
let PathTracked(dex) = reikland::from_bytes::<PathTracked<TypeMap>>(TYPES_DAT)
.unwrap_or_else(|e| panic!("types.dat failed to deserialize: {e}"));
assert!(!dex.0.is_empty());
assert_normal(dex.0.first().unwrap().1);
}
fn assert_normal(t: &Type) {
assert_eq!(t.id, "NORMAL");
assert_eq!(t.id_number, 0);
assert_eq!(t.real_name, "Normal");
assert!(!t.pseudo_type);
assert!(!t.special_type);
assert_eq!(t.weaknesses, vec!["FIGHTING"]);
assert!(t.resistances.is_empty());
assert_eq!(t.immunities, vec!["GHOST"]);
}
}