Skip to main content

steel_registry/data_components/components/
equippable.rs

1//! Equippable component for armor and equipment items.
2
3use std::io::{Result, Write};
4
5use steel_utils::{
6    hash::{ComponentHasher, HashComponent},
7    serial::{ReadFrom, WriteTo},
8};
9
10/// Equipment slot for the equippable component.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub enum EquippableSlot {
13    Head,
14    Chest,
15    Legs,
16    Feet,
17    Body,
18    Mainhand,
19    Offhand,
20    Saddle,
21}
22
23impl EquippableSlot {
24    /// Parses an equipment slot from a string (as used in items.json).
25    #[must_use]
26    pub fn parse(s: &str) -> Option<Self> {
27        match s {
28            "head" => Some(Self::Head),
29            "chest" => Some(Self::Chest),
30            "legs" => Some(Self::Legs),
31            "feet" => Some(Self::Feet),
32            "body" => Some(Self::Body),
33            "mainhand" => Some(Self::Mainhand),
34            "offhand" => Some(Self::Offhand),
35            "saddle" => Some(Self::Saddle),
36            _ => None,
37        }
38    }
39
40    /// Returns the string representation of this slot.
41    #[must_use]
42    pub const fn as_str(&self) -> &'static str {
43        match self {
44            Self::Head => "head",
45            Self::Chest => "chest",
46            Self::Legs => "legs",
47            Self::Feet => "feet",
48            Self::Body => "body",
49            Self::Mainhand => "mainhand",
50            Self::Offhand => "offhand",
51            Self::Saddle => "saddle",
52        }
53    }
54
55    /// Returns true if this is a humanoid armor slot.
56    #[must_use]
57    pub const fn is_humanoid_armor(&self) -> bool {
58        matches!(self, Self::Head | Self::Chest | Self::Legs | Self::Feet)
59    }
60}
61
62/// The equippable component data.
63#[derive(Debug, Clone, PartialEq)]
64pub struct Equippable {
65    pub slot: EquippableSlot,
66}
67
68impl WriteTo for Equippable {
69    fn write(&self, _writer: &mut impl Write) -> Result<()> {
70        // TODO: Implement proper Equippable serialization
71        // Format: slot (VarInt), equip_sound (SoundEvent), model (Optional), camera_overlay (Optional),
72        //         allowed_entities (Optional HolderSet), dispensable (bool), swappable (bool),
73        //         damage_on_hurt (bool), equip_on_interact (bool)
74        Ok(())
75    }
76}
77
78impl ReadFrom for Equippable {
79    fn read(_data: &mut std::io::Cursor<&[u8]>) -> Result<Self> {
80        // TODO: Implement proper Equippable deserialization
81        Ok(Self {
82            slot: EquippableSlot::Chest,
83        })
84    }
85}
86
87impl HashComponent for Equippable {
88    fn hash_component(&self, hasher: &mut ComponentHasher) {
89        // Equippable is hashed as a map
90        // For now, hash as empty map since full implementation requires proper codec
91        hasher.start_map();
92        // TODO: Add proper field hashing when Equippable codec is implemented
93        hasher.end_map();
94    }
95}
96
97impl simdnbt::ToNbtTag for Equippable {
98    fn to_nbt_tag(self) -> simdnbt::owned::NbtTag {
99        use simdnbt::owned::{NbtCompound, NbtTag};
100
101        let mut compound = NbtCompound::new();
102        compound.insert("slot", self.slot.as_str());
103        NbtTag::Compound(compound)
104    }
105}
106
107impl simdnbt::FromNbtTag for Equippable {
108    fn from_nbt_tag(tag: simdnbt::borrow::NbtTag) -> Option<Self> {
109        let compound = tag.compound()?;
110        let slot_str = compound.get("slot")?.string()?.to_str();
111        let slot = EquippableSlot::parse(&slot_str)?;
112        Some(Self { slot })
113    }
114}