enshwick 1.0.0-alpha.8

The Enshwick Loader's modding API for Enshrouded: the one crate mods are written against
Documentation
//! What the item a recipe makes is, as the game's item data classes it.

use super::super::Guid;

/// What the item a recipe makes is, as the game classes it, from
/// [`RecipeView::kind`](super::RecipeView::kind); the game may class more
/// items in later builds.
///
/// The name is enshwick's: the game has no recipe kind; it follows from the
/// output item's equipment slot.
///
/// # Examples
///
/// See [`RecipeView::kind`](super::RecipeView::kind).
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum RecipeKind {
    /// A building block, such as a wall or floor block.
    BuildingBlock,
    /// A roof block.
    Roof,
    /// An overgrowth material.
    Overgrowth,
    /// A terrain material.
    Terrain,
    /// Anything else: equipment, props, furniture and decorations among them.
    Other,
}

impl RecipeKind {
    /// Every kind, in the order of their codes.
    ///
    /// # Examples
    ///
    /// ```
    /// use enshwick::content::recipes::RecipeKind;
    ///
    /// assert!(RecipeKind::ALL.contains(&RecipeKind::Roof));
    /// ```
    pub const ALL: &[Self] = &[
        Self::Other,
        Self::BuildingBlock,
        Self::Roof,
        Self::Overgrowth,
        Self::Terrain,
    ];

    /// The kind's name in a data pack, such as `building-block`.
    ///
    /// # Examples
    ///
    /// ```
    /// use enshwick::content::recipes::RecipeKind;
    ///
    /// assert_eq!(RecipeKind::BuildingBlock.name(), "building-block");
    /// ```
    #[must_use]
    pub const fn name(self) -> &'static str {
        match self {
            Self::BuildingBlock => "building-block",
            Self::Roof => "roof",
            Self::Overgrowth => "overgrowth",
            Self::Terrain => "terrain",
            Self::Other => "other",
        }
    }

    /// The kind [`RecipeKind::name`] calls `name`.
    ///
    /// # Examples
    ///
    /// ```
    /// use enshwick::content::recipes::RecipeKind;
    ///
    /// assert_eq!(RecipeKind::named("roof"), Some(RecipeKind::Roof));
    /// ```
    #[must_use]
    pub fn named(name: &str) -> Option<Self> {
        Self::ALL.iter().copied().find(|kind| kind.name() == name)
    }

    /// The loader's code for this kind.
    ///
    /// # Examples
    ///
    /// ```
    /// use enshwick::abi;
    /// use enshwick::content::recipes::RecipeKind;
    ///
    /// assert_eq!(RecipeKind::Roof.code(), abi::RecipeKind::ROOF);
    /// ```
    #[must_use]
    pub const fn code(self) -> crate::abi::RecipeKind {
        use crate::abi::RecipeKind as Abi;
        match self {
            Self::BuildingBlock => Abi::BUILDING_BLOCK,
            Self::Roof => Abi::ROOF,
            Self::Overgrowth => Abi::OVERGROWTH,
            Self::Terrain => Abi::TERRAIN,
            Self::Other => Abi::OTHER,
        }
    }

    /// The kind of the item `item`, as the loader answers it.
    pub(super) fn of(item: Guid) -> Option<Self> {
        let host = crate::host()?;
        let mut out = crate::abi::RecipeKind::OTHER;
        (host.api().recipe_kind)(host.handle(), item.id(), &raw mut out)
            .is_ok()
            .then(|| out.into())
    }
}

/// A kind code a newer loader may add reads as [`RecipeKind::Other`].
impl From<crate::abi::RecipeKind> for RecipeKind {
    fn from(kind: crate::abi::RecipeKind) -> Self {
        use crate::abi::RecipeKind as Abi;
        match kind {
            Abi::BUILDING_BLOCK => Self::BuildingBlock,
            Abi::ROOF => Self::Roof,
            Abi::OVERGROWTH => Self::Overgrowth,
            Abi::TERRAIN => Self::Terrain,
            _ => Self::Other,
        }
    }
}

impl From<RecipeKind> for crate::abi::RecipeKind {
    fn from(kind: RecipeKind) -> Self {
        kind.code()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn every_kind_reads_back_from_its_name() {
        for kind in RecipeKind::ALL {
            assert_eq!(RecipeKind::named(kind.name()), Some(*kind));
        }
        assert_eq!(RecipeKind::named("Roof"), None);
    }
}