steel_registry/data_components/components/
painting_variant.rs1use std::io::{Cursor, Error, Result, Write};
4use std::str::FromStr;
5
6use simdnbt::FromNbtTag;
7use simdnbt::owned::NbtTag;
8use steel_utils::Identifier;
9use steel_utils::hash::{ComponentHasher, HashComponent};
10use steel_utils::serial::{ReadFrom, WriteTo};
11
12use crate::painting_variant::{PaintingVariant, PaintingVariantRef, PaintingVariantValue};
13use crate::{REGISTRY, RegistryExt, RegistryHolder};
14
15#[derive(Debug, Clone, PartialEq)]
18pub struct PaintingVariantComponent {
19 variant: RegistryHolder<PaintingVariant>,
20}
21
22impl PaintingVariantComponent {
23 #[must_use]
24 pub const fn new(variant: PaintingVariantRef) -> Self {
25 Self {
26 variant: RegistryHolder::reference(variant),
27 }
28 }
29
30 #[must_use]
31 pub const fn direct(variant: PaintingVariantValue) -> Self {
32 Self {
33 variant: RegistryHolder::direct(variant),
34 }
35 }
36
37 #[must_use]
38 pub const fn variant(&self) -> &RegistryHolder<PaintingVariant> {
39 &self.variant
40 }
41}
42
43impl WriteTo for PaintingVariantComponent {
44 fn write(&self, writer: &mut impl Write) -> Result<()> {
45 self.variant.write(writer)
46 }
47}
48
49impl ReadFrom for PaintingVariantComponent {
50 fn read(data: &mut Cursor<&[u8]>) -> Result<Self> {
51 RegistryHolder::read(data).map(|variant| Self { variant })
52 }
53}
54
55impl FromNbtTag for PaintingVariantComponent {
56 fn from_nbt_tag(tag: simdnbt::borrow::NbtTag) -> Option<Self> {
57 let key = Identifier::from_str(&tag.string()?.to_str()).ok()?;
58 REGISTRY.painting_variants.by_key(&key).map(Self::new)
59 }
60}
61
62impl HashComponent for PaintingVariantComponent {
63 fn hash_component(&self, hasher: &mut ComponentHasher) {
64 self.variant.hash_component(hasher);
65 }
66}
67
68impl PaintingVariantComponent {
69 pub(crate) fn try_to_persistent_nbt(&self) -> Result<NbtTag> {
70 let Some(variant) = self.variant.as_reference() else {
71 return Err(Error::other(
72 "Direct painting variant holder is not persistent",
73 ));
74 };
75 Ok(NbtTag::String(variant.key.to_string().into()))
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use std::io::Cursor;
82
83 use simdnbt::FromNbtTag as _;
84 use simdnbt::borrow::read_tag;
85 use steel_utils::hash::HashComponent as _;
86 use steel_utils::serial::{ReadFrom as _, WriteTo as _};
87
88 use super::PaintingVariantComponent;
89 use crate::init_vanilla_registry;
90 use crate::vanilla_painting_variants;
91
92 #[test]
93 fn registry_reference_round_trips_both_codecs() {
94 init_vanilla_registry();
95 let component = PaintingVariantComponent::new(&vanilla_painting_variants::KEBAB);
96
97 let mut network = Vec::new();
98 component
99 .write(&mut network)
100 .expect("variant should encode");
101 assert_eq!(
102 PaintingVariantComponent::read(&mut Cursor::new(network.as_slice()))
103 .expect("variant should decode"),
104 component
105 );
106
107 let nbt = component
108 .try_to_persistent_nbt()
109 .expect("reference is persistent");
110 assert_eq!(component.compute_hash(), nbt.compute_hash());
111 let mut bytes = Vec::new();
112 nbt.write(&mut bytes);
113 let borrowed =
114 read_tag(&mut Cursor::new(bytes.as_slice())).expect("variant NBT should parse");
115 assert_eq!(
116 PaintingVariantComponent::from_nbt_tag(borrowed.as_tag()),
117 Some(component)
118 );
119 }
120
121 #[test]
122 fn direct_stream_holder_round_trips_but_is_not_persistent() {
123 use crate::painting_variant::PaintingVariantValue;
124 use steel_utils::Identifier;
125
126 let component = PaintingVariantComponent::direct(PaintingVariantValue {
127 width: -1,
128 height: 32,
129 asset_id: Identifier::vanilla_static("custom"),
130 title: None,
131 author: None,
132 });
133 let mut network = Vec::new();
134 component
135 .write(&mut network)
136 .expect("direct variant should encode");
137 assert_eq!(
138 PaintingVariantComponent::read(&mut Cursor::new(network.as_slice()))
139 .expect("direct variant should decode"),
140 component
141 );
142 assert!(component.try_to_persistent_nbt().is_err());
143 }
144}