Skip to main content

steel_registry/registry/
holder.rs

1//! Registry holders used by Vanilla registry-aware codecs.
2
3use 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
15/// Registry operations and direct value type required by [`RegistryHolder`].
16pub trait RegistryHolderEntry: RegistryEntry + Debug + Send + Sync {
17    /// The value stored in the registry entry and carried by a direct holder.
18    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    /// Human-readable registry name used in codec errors.
31    const REGISTRY_NAME: &'static str;
32
33    /// Returns the entry's registry-independent value.
34    fn holder_value(&self) -> &Self::Value;
35
36    /// Looks up a registry reference by its protocol ID.
37    fn holder_by_id(id: usize) -> Option<&'static Self>;
38
39    /// Looks up a registry reference by its key.
40    fn holder_by_key(key: &Identifier) -> Option<&'static Self>;
41}
42
43/// Vanilla `Holder<T>` for registries whose codecs permit inline values.
44#[derive(Debug)]
45pub enum RegistryHolder<T: RegistryHolderEntry> {
46    /// A value owned by the target registry.
47    Reference(&'static T),
48    /// A complete value embedded directly in the holder.
49    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}