steel_core/inventory/container/
helper.rs1use simdnbt::borrow::NbtCompound as NbtCompoundView;
4use steel_registry::item_stack::ItemStack;
5use steel_utils::nbt::NbtNumeric as _;
6
7pub fn load_all_items(nbt: NbtCompoundView<'_, '_>, items: &mut [ItemStack]) {
10 let Some(entries) = nbt.list("Items").and_then(|list| list.compounds()) else {
11 return;
12 };
13 for entry in entries {
14 let Some(slot) = item_slot(&entry) else {
15 continue;
16 };
17 if let Some(target) = items.get_mut(slot)
18 && let Some(stack) = ItemStack::from_borrowed_compound(&entry)
19 {
20 *target = stack;
21 }
22 }
23}
24
25fn item_slot(entry: &NbtCompoundView<'_, '_>) -> Option<usize> {
27 let Some(tag) = entry.get("Slot") else {
28 return Some(0);
29 };
30 tag.codec_i8().map(|slot| usize::from(slot as u8))
31}
32
33#[cfg(test)]
34mod tests {
35 use std::io::Cursor;
36
37 use simdnbt::borrow::read_compound;
38 use simdnbt::owned::{NbtCompound, NbtList, NbtTag};
39 use steel_registry::{init_vanilla_registry, vanilla_items};
40
41 use super::*;
42
43 fn item_entry(slot: Option<NbtTag>) -> NbtCompound {
44 let mut entry = NbtCompound::new();
45 entry.insert("id", "minecraft:stone");
46 entry.insert("count", 1);
47 if let Some(slot) = slot {
48 entry.insert("Slot", slot);
49 }
50 entry
51 }
52
53 fn load(entries: Vec<NbtCompound>) -> Vec<ItemStack> {
54 init_vanilla_registry();
55 let mut nbt = NbtCompound::new();
56 nbt.insert("Items", NbtList::Compound(entries));
57 let mut bytes = Vec::new();
58 nbt.write(&mut bytes);
59 let borrowed =
60 read_compound(&mut Cursor::new(bytes.as_slice())).expect("test NBT should reborrow");
61
62 let mut items = vec![ItemStack::empty(); 3];
63 load_all_items((&borrowed).into(), &mut items);
64 items
65 }
66
67 #[test]
68 fn missing_slot_defaults_to_zero() {
69 assert!(load(vec![item_entry(None)])[0].is(&vanilla_items::STONE));
70 }
71
72 #[test]
73 fn numeric_slot_narrows_to_a_byte() {
74 let items = load(vec![item_entry(Some(NbtTag::Int(0x102)))]);
75 assert!(items[2].is(&vanilla_items::STONE));
76 }
77
78 #[test]
79 fn negative_slot_is_unsigned_and_out_of_range() {
80 let items = load(vec![item_entry(Some(NbtTag::Byte(-1)))]);
81 assert!(items.iter().all(ItemStack::is_empty));
82 }
83
84 #[test]
85 fn non_numeric_slot_rejects_the_entry() {
86 let items = load(vec![item_entry(Some(NbtTag::String("0".into())))]);
87 assert!(items.iter().all(ItemStack::is_empty));
88 }
89}