steel_registry/data_components/components/
provides_trim_material.rs1use std::io::{Cursor, Result, Write};
4
5use simdnbt::{FromNbtTag, ToNbtTag};
6use steel_utils::hash::{ComponentHasher, HashComponent};
7use steel_utils::serial::{ReadFrom, WriteTo};
8
9use crate::RegistryHolder;
10use crate::trim_material::{TrimMaterial, TrimMaterialValue};
11
12#[derive(Debug, Clone, PartialEq)]
14pub struct ProvidesTrimMaterial {
15 material: RegistryHolder<TrimMaterial>,
16}
17
18impl ProvidesTrimMaterial {
19 #[must_use]
20 pub const fn new(material: RegistryHolder<TrimMaterial>) -> Self {
21 Self { material }
22 }
23
24 #[must_use]
25 pub const fn material(&self) -> &RegistryHolder<TrimMaterial> {
26 &self.material
27 }
28
29 #[must_use]
30 pub fn value(&self) -> &TrimMaterialValue {
31 self.material.value()
32 }
33}
34
35impl WriteTo for ProvidesTrimMaterial {
36 fn write(&self, writer: &mut impl Write) -> Result<()> {
37 self.material.write(writer)
38 }
39}
40
41impl ReadFrom for ProvidesTrimMaterial {
42 fn read(data: &mut Cursor<&[u8]>) -> Result<Self> {
43 RegistryHolder::read(data).map(Self::new)
44 }
45}
46
47impl ToNbtTag for ProvidesTrimMaterial {
48 fn to_nbt_tag(self) -> simdnbt::owned::NbtTag {
49 self.material.to_nbt_tag()
50 }
51}
52
53impl FromNbtTag for ProvidesTrimMaterial {
54 fn from_nbt_tag(tag: simdnbt::borrow::NbtTag) -> Option<Self> {
55 RegistryHolder::from_nbt_tag(tag).map(Self::new)
56 }
57}
58
59impl HashComponent for ProvidesTrimMaterial {
60 fn hash_component(&self, hasher: &mut ComponentHasher) {
61 self.material.hash_component(hasher);
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use std::io::Cursor;
68
69 use simdnbt::borrow::read_tag;
70 use simdnbt::{FromNbtTag as _, ToNbtTag as _};
71 use steel_utils::Identifier;
72 use steel_utils::hash::HashComponent as _;
73 use steel_utils::serial::{ReadFrom as _, WriteTo as _};
74 use text_components::{TextComponent, format::Color};
75
76 use super::ProvidesTrimMaterial;
77 use crate::RegistryHolder;
78 use crate::data_components::vanilla_components::PROVIDES_TRIM_MATERIAL;
79 use crate::init_vanilla_registry;
80 use crate::item_stack::ItemStack;
81 use crate::trim_material::TrimMaterialValue;
82 use crate::{REGISTRY, vanilla_items, vanilla_trim_materials};
83
84 fn parse_component(tag: simdnbt::owned::NbtTag) -> Option<ProvidesTrimMaterial> {
85 let mut bytes = Vec::new();
86 tag.write(&mut bytes);
87 let borrowed = read_tag(&mut Cursor::new(bytes.as_slice())).ok()?;
88 ProvidesTrimMaterial::from_nbt_tag(borrowed.as_tag())
89 }
90
91 fn inline_component() -> ProvidesTrimMaterial {
92 let mut description = TextComponent::plain("Custom trim material");
93 description.format.color = Some(Color::Rgb(0x12, 0x34, 0x56));
94 ProvidesTrimMaterial::new(RegistryHolder::direct(TrimMaterialValue::new(
95 Identifier::vanilla_static("trim/custom"),
96 description,
97 )))
98 }
99
100 #[test]
101 fn registry_reference_round_trips_both_codecs() {
102 init_vanilla_registry();
103 let component =
104 ProvidesTrimMaterial::new(RegistryHolder::reference(&vanilla_trim_materials::IRON));
105
106 let mut network = Vec::new();
107 component
108 .write(&mut network)
109 .expect("registry trim material should encode");
110 assert_eq!(
111 ProvidesTrimMaterial::read(&mut Cursor::new(network.as_slice()))
112 .expect("registry trim material should decode"),
113 component
114 );
115
116 let nbt = component.clone().to_nbt_tag();
117 assert_eq!(nbt, simdnbt::owned::NbtTag::String("minecraft:iron".into()));
118 assert_eq!(parse_component(nbt), Some(component));
119 }
120
121 #[test]
122 fn inline_material_round_trips_and_hashes_its_flattened_record() {
123 init_vanilla_registry();
124 let component = inline_component();
125
126 let mut network = Vec::new();
127 component
128 .write(&mut network)
129 .expect("inline trim material should encode");
130 assert_eq!(
131 ProvidesTrimMaterial::read(&mut Cursor::new(network.as_slice()))
132 .expect("inline trim material should decode"),
133 component
134 );
135
136 let nbt = component.clone().to_nbt_tag();
137 assert_eq!(parse_component(nbt.clone()), Some(component.clone()));
138 assert_eq!(component.compute_hash(), nbt.compute_hash());
139
140 let simdnbt::owned::NbtTag::Compound(compound) = nbt else {
141 panic!("inline trim material should encode as a compound");
142 };
143 assert_eq!(
144 compound
145 .compound("description")
146 .and_then(|description| description.string("color"))
147 .map(|color| color.to_str().into_owned()),
148 Some("#123456".to_owned())
149 );
150 }
151
152 #[test]
153 fn extracted_item_prototypes_reference_every_vanilla_trim_material() {
154 init_vanilla_registry();
155 let prototypes = [
156 (
157 &*vanilla_items::REDSTONE,
158 &*vanilla_trim_materials::REDSTONE,
159 ),
160 (&*vanilla_items::DIAMOND, &*vanilla_trim_materials::DIAMOND),
161 (&*vanilla_items::EMERALD, &*vanilla_trim_materials::EMERALD),
162 (
163 &*vanilla_items::LAPIS_LAZULI,
164 &*vanilla_trim_materials::LAPIS,
165 ),
166 (&*vanilla_items::QUARTZ, &*vanilla_trim_materials::QUARTZ),
167 (
168 &*vanilla_items::AMETHYST_SHARD,
169 &*vanilla_trim_materials::AMETHYST,
170 ),
171 (&*vanilla_items::IRON_INGOT, &*vanilla_trim_materials::IRON),
172 (
173 &*vanilla_items::COPPER_INGOT,
174 &*vanilla_trim_materials::COPPER,
175 ),
176 (&*vanilla_items::GOLD_INGOT, &*vanilla_trim_materials::GOLD),
177 (
178 &*vanilla_items::NETHERITE_INGOT,
179 &*vanilla_trim_materials::NETHERITE,
180 ),
181 (
182 &*vanilla_items::RESIN_BRICK,
183 &*vanilla_trim_materials::RESIN,
184 ),
185 ];
186
187 assert_eq!(
188 REGISTRY
189 .items
190 .iter()
191 .filter(|(_, item)| item.components.has(PROVIDES_TRIM_MATERIAL))
192 .count(),
193 prototypes.len()
194 );
195 for (item, material) in prototypes {
196 assert_eq!(
197 ItemStack::new(item)
198 .get(PROVIDES_TRIM_MATERIAL)
199 .and_then(|component| component.material().as_reference()),
200 Some(material)
201 );
202 }
203 }
204}