valence_protocol/
item.rs

1use std::io::Write;
2
3pub use valence_generated::item::ItemKind;
4use valence_nbt::Compound;
5
6use crate::{Decode, Encode};
7
8/// A stack of items in an inventory.
9#[derive(Clone, PartialEq, Debug, Default)]
10pub struct ItemStack {
11    pub item: ItemKind,
12    pub count: i8,
13    pub nbt: Option<Compound>,
14}
15
16impl ItemStack {
17    pub const EMPTY: ItemStack = ItemStack {
18        item: ItemKind::Air,
19        count: 0,
20        nbt: None,
21    };
22
23    #[must_use]
24    pub const fn new(item: ItemKind, count: i8, nbt: Option<Compound>) -> Self {
25        Self { item, count, nbt }
26    }
27
28    #[must_use]
29    pub const fn with_count(mut self, count: i8) -> Self {
30        self.count = count;
31        self
32    }
33
34    #[must_use]
35    pub const fn with_item(mut self, item: ItemKind) -> Self {
36        self.item = item;
37        self
38    }
39
40    #[must_use]
41    pub fn with_nbt<C: Into<Option<Compound>>>(mut self, nbt: C) -> Self {
42        self.nbt = nbt.into();
43        self
44    }
45
46    pub const fn is_empty(&self) -> bool {
47        matches!(self.item, ItemKind::Air) || self.count <= 0
48    }
49}
50
51impl Encode for ItemStack {
52    fn encode(&self, mut w: impl Write) -> anyhow::Result<()> {
53        if self.is_empty() {
54            false.encode(w)
55        } else {
56            true.encode(&mut w)?;
57            self.item.encode(&mut w)?;
58            self.count.encode(&mut w)?;
59            match &self.nbt {
60                Some(n) => n.encode(w),
61                None => 0_u8.encode(w),
62            }
63        }
64    }
65}
66
67impl Decode<'_> for ItemStack {
68    fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
69        let present = bool::decode(r)?;
70        if !present {
71            return Ok(ItemStack::EMPTY);
72        };
73
74        let item = ItemKind::decode(r)?;
75        let count = i8::decode(r)?;
76
77        let nbt = if let [0, rest @ ..] = *r {
78            *r = rest;
79            None
80        } else {
81            Some(Compound::decode(r)?)
82        };
83
84        let stack = ItemStack { item, count, nbt };
85
86        // Normalize empty item stacks.
87        if stack.is_empty() {
88            Ok(ItemStack::EMPTY)
89        } else {
90            Ok(stack)
91        }
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[test]
100    fn empty_item_stack_is_empty() {
101        let air_stack = ItemStack::new(ItemKind::Air, 10, None);
102        let less_then_one_stack = ItemStack::new(ItemKind::Stone, 0, None);
103
104        assert!(air_stack.is_empty());
105        assert!(less_then_one_stack.is_empty());
106
107        assert!(ItemStack::EMPTY.is_empty());
108
109        let not_empty_stack = ItemStack::new(ItemKind::Stone, 10, None);
110
111        assert!(!not_empty_stack.is_empty());
112    }
113}