infinite-fusion 0.2.0

A simple crate for reading Pokemon Infinite Fusion's data
Documentation
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 Ability {
    #[serde(rename = "@id")]
    pub id: String,
    #[serde(rename = "@id_number")]
    pub id_number: u8,
    #[serde(rename = "@real_name")]
    pub real_name: String,
    #[serde(rename = "@real_description", deserialize_with = "as_transparent")]
    pub real_description: String,
}

/// [`Deserialize`] implementation for `abilities.dat`
#[derive(Debug, Clone, Serialize)]
pub struct AbilityDex(pub Vec<Ability>);

impl<'de> serde::Deserialize<'de> for AbilityDex {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        reikland::DualKeyVec::<(Ability, Ignored)>::deserialize(deserializer)
            .map(|reikland::DualKeyVec(mut d)| {
                d.sort_by_key(|(a, _)| a.id_number);
                d.into_iter().map(|(a, _)| a).collect()
            })
            .map(AbilityDex)
    }
}

/// [`Deserialize`] implementation for `abilities.dat`, indexable by key or value
#[derive(Debug, Clone, Serialize)]
pub struct AbilityMap(pub IndexMap<String, Ability>);

impl<'de> serde::Deserialize<'de> for AbilityMap {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        AbilityDex::deserialize(deserializer)
            .map(|AbilityDex(v)| AbilityMap(v.into_iter().map(|a| (a.id.clone(), a)).collect()))
    }
}

#[cfg(test)]
mod tests {
    use crate::test::PathTracked;

    use super::*;

    const ABILITIES_DAT: &[u8] = include_bytes!("../InfiniteFusion/Data/abilities.dat");

    #[test]
    fn deserialize_abilities_dat_vec() {
        let PathTracked(dex) = reikland::from_bytes::<PathTracked<AbilityDex>>(ABILITIES_DAT)
            .unwrap_or_else(|e| panic!("abilities.dat failed to deserialize: {e}"));

        assert!(!dex.0.is_empty());
        assert_stench(dex.0.first().unwrap());
    }

    #[test]
    fn deserialize_abilities_dat_map() {
        let PathTracked(dex) = reikland::from_bytes::<PathTracked<AbilityMap>>(ABILITIES_DAT)
            .unwrap_or_else(|e| panic!("abilities.dat failed to deserialize: {e}"));

        assert!(!dex.0.is_empty());
        assert_stench(dex.0.first().unwrap().1);
    }

    fn assert_stench(a: &Ability) {
        assert_eq!(a.id, "STENCH");
        assert_eq!(a.id_number, 1);
        assert_eq!(a.real_name, "Stench");
        assert_eq!(
            a.real_description,
            "The stench may cause the target to flinch."
        );
    }
}