steel_registry/registry/
holder.rs1use std::fmt::Debug;
4use std::io::{Cursor, Error, Result, Write};
5use std::str::FromStr;
6
7use simdnbt::{FromNbtTag, ToNbtTag};
8use steel_utils::Identifier;
9use steel_utils::codec::VarInt;
10use steel_utils::hash::{ComponentHasher, HashComponent};
11use steel_utils::serial::{ReadFrom, WriteTo};
12
13use crate::RegistryEntry;
14
15pub trait RegistryHolderEntry: RegistryEntry + Debug + Send + Sync {
17 type Value: Clone
19 + Debug
20 + PartialEq
21 + Send
22 + Sync
23 + WriteTo
24 + ReadFrom
25 + ToNbtTag
26 + FromNbtTag
27 + HashComponent
28 + 'static;
29
30 const REGISTRY_NAME: &'static str;
32
33 fn holder_value(&self) -> &Self::Value;
35
36 fn holder_by_id(id: usize) -> Option<&'static Self>;
38
39 fn holder_by_key(key: &Identifier) -> Option<&'static Self>;
41}
42
43#[derive(Debug)]
45pub enum RegistryHolder<T: RegistryHolderEntry> {
46 Reference(&'static T),
48 Direct(T::Value),
50}
51
52impl<T: RegistryHolderEntry> Clone for RegistryHolder<T> {
53 fn clone(&self) -> Self {
54 match self {
55 Self::Reference(value) => Self::Reference(value),
56 Self::Direct(value) => Self::Direct(value.clone()),
57 }
58 }
59}
60
61impl<T: RegistryHolderEntry> PartialEq for RegistryHolder<T> {
62 fn eq(&self, other: &Self) -> bool {
63 match (self, other) {
64 (Self::Reference(left), Self::Reference(right)) => *left == *right,
65 (Self::Direct(left), Self::Direct(right)) => left == right,
66 (Self::Reference(_), Self::Direct(_)) | (Self::Direct(_), Self::Reference(_)) => false,
67 }
68 }
69}
70
71impl<T: RegistryHolderEntry> RegistryHolder<T> {
72 #[must_use]
73 pub const fn reference(value: &'static T) -> Self {
74 Self::Reference(value)
75 }
76
77 #[must_use]
78 pub const fn direct(value: T::Value) -> Self {
79 Self::Direct(value)
80 }
81
82 #[must_use]
83 pub fn value(&self) -> &T::Value {
84 match self {
85 Self::Reference(value) => value.holder_value(),
86 Self::Direct(value) => value,
87 }
88 }
89
90 #[must_use]
91 pub const fn as_reference(&self) -> Option<&'static T> {
92 match self {
93 Self::Reference(value) => Some(value),
94 Self::Direct(_) => None,
95 }
96 }
97
98 #[must_use]
99 pub const fn as_direct(&self) -> Option<&T::Value> {
100 match self {
101 Self::Reference(_) => None,
102 Self::Direct(value) => Some(value),
103 }
104 }
105}
106
107impl<T: RegistryHolderEntry> WriteTo for RegistryHolder<T> {
108 fn write(&self, writer: &mut impl Write) -> Result<()> {
109 match self {
110 Self::Reference(value) => {
111 let id = value.try_id().ok_or_else(|| {
112 Error::other(format!("Unknown {}: {}", T::REGISTRY_NAME, value.key()))
113 })?;
114 let id = i32::try_from(id).map_err(|_| {
115 Error::other(format!(
116 "{} id out of protocol range: {id}",
117 T::REGISTRY_NAME
118 ))
119 })?;
120 let encoded_id = id.checked_add(1).ok_or_else(|| {
121 Error::other(format!("{} id exceeds protocol range", T::REGISTRY_NAME))
122 })?;
123 VarInt(encoded_id).write(writer)
124 }
125 Self::Direct(value) => {
126 VarInt(0).write(writer)?;
127 value.write(writer)
128 }
129 }
130 }
131}
132
133impl<T: RegistryHolderEntry> ReadFrom for RegistryHolder<T> {
134 fn read(data: &mut Cursor<&[u8]>) -> Result<Self> {
135 let encoded_id = VarInt::read(data)?.0;
136 if encoded_id == 0 {
137 return T::Value::read(data).map(Self::Direct);
138 }
139
140 let id = encoded_id
141 .checked_sub(1)
142 .and_then(|id| usize::try_from(id).ok())
143 .ok_or_else(|| {
144 Error::other(format!(
145 "Invalid {} holder id: {encoded_id}",
146 T::REGISTRY_NAME
147 ))
148 })?;
149 T::holder_by_id(id).map(Self::Reference).ok_or_else(|| {
150 Error::other(format!(
151 "Unknown {} holder id: {encoded_id}",
152 T::REGISTRY_NAME
153 ))
154 })
155 }
156}
157
158impl<T: RegistryHolderEntry> ToNbtTag for RegistryHolder<T> {
159 fn to_nbt_tag(self) -> simdnbt::owned::NbtTag {
160 match self {
161 Self::Reference(value) => value.key().to_string().to_nbt_tag(),
162 Self::Direct(value) => value.to_nbt_tag(),
163 }
164 }
165}
166
167impl<T: RegistryHolderEntry> FromNbtTag for RegistryHolder<T> {
168 fn from_nbt_tag(tag: simdnbt::borrow::NbtTag) -> Option<Self> {
169 if let Some(value) = tag.string() {
170 let key = Identifier::from_str(&value.to_str()).ok()?;
171 return T::holder_by_key(&key).map(Self::Reference);
172 }
173
174 T::Value::from_nbt_tag(tag).map(Self::Direct)
175 }
176}
177
178impl<T: RegistryHolderEntry> HashComponent for RegistryHolder<T> {
179 fn hash_component(&self, hasher: &mut ComponentHasher) {
180 match self {
181 Self::Reference(value) => value.key().to_string().hash_component(hasher),
182 Self::Direct(value) => value.hash_component(hasher),
183 }
184 }
185}