Skip to main content

steel_registry/data_components/
component_data.rs

1//! Type-erased data component values.
2
3use std::fmt::{self, Debug, Formatter};
4
5use steel_utils::{Downcast as _, DowncastType, DowncastTypeKey, ErasedType};
6
7use super::components::{
8    ArmorTrim, AttackRange, BannerPatternLayers, Bees, BlockEntityData, BlockItemStateProperties,
9    BlocksAttacks, BundleContents, ChargedProjectiles, Consumable, CustomData, CustomModelData,
10    DamageResistant, DamageTypeComponent, DeathProtection, DebugStickState, DyedItemColor,
11    Enchantable, EntityData, Equippable, FireworkExplosion, Fireworks, FoodProperties,
12    InstrumentComponent, ItemAttributeModifiers, ItemContainerContents, ItemEnchantments, ItemLore,
13    JukeboxPlayable, KineticWeapon, LodestoneTracker, MapDecorations, MapId, MapItemColor,
14    MapPostProcessing, OminousBottleAmplifier, PaintingVariantComponent, PiercingWeapon,
15    PotDecorations, PotionContents, ProvidesBannerPatterns, ProvidesTrimMaterial, Rarity, Recipes,
16    Repairable, SeededContainerLoot, SulfurCubeContent, SuspiciousStewEffects, SwingAnimation,
17    Tool, TooltipDisplay, UseCooldown, UseEffects, UseRemainder, Weapon, WritableBookContent,
18    WrittenBookContent,
19};
20use crate::cat_sound_variant::CatSoundVariant;
21use crate::cat_variant::CatVariant;
22use crate::chicken_sound_variant::ChickenSoundVariant;
23use crate::chicken_variant::ChickenVariant;
24use crate::cow_sound_variant::CowSoundVariant;
25use crate::cow_variant::CowVariant;
26use crate::frog_variant::FrogVariant;
27use crate::item_predicate::{AdventureModePredicate, LockCode};
28use crate::pig_sound_variant::PigSoundVariant;
29use crate::pig_variant::PigVariant;
30use crate::resolvable_profile::ResolvableProfile;
31use crate::villager_type::VillagerType;
32use crate::wolf_sound_variant::WolfSoundVariant;
33use crate::wolf_variant::WolfVariant;
34use crate::zombie_nautilus_variant::ZombieNautilusVariant;
35use crate::{
36    AxolotlVariant, DyeColor, FoxVariant, HorseVariant, LlamaVariant, MooshroomVariant,
37    ParrotVariant, RabbitVariant, RegistryReference, SalmonVariant, TropicalFishPattern,
38};
39
40/// Behavior required from a value stored in a [`ComponentData`].
41///
42/// Concrete type recovery is provided by Steel's deterministic keyed
43/// downcasting foundation. A value is eligible for the blanket implementation
44/// when it also supports cloning, comparison, debugging, and shared server
45/// access. Persistent-codec hashing is registered separately so transient
46/// values do not need a fake hash representation.
47pub trait Component: ErasedType + Debug + Send + Sync + 'static {
48    #[doc(hidden)]
49    fn clone_component(&self) -> Box<dyn Component>;
50
51    #[doc(hidden)]
52    fn component_eq(&self, other: &dyn Component) -> bool;
53}
54
55impl<T> Component for T
56where
57    T: DowncastType + Clone + Debug + PartialEq + Send + Sync,
58{
59    fn clone_component(&self) -> Box<dyn Component> {
60        Box::new(self.clone())
61    }
62
63    fn component_eq(&self, other: &dyn Component) -> bool {
64        other.downcast_ref::<T>() == Some(self)
65    }
66}
67
68/// A type-erased component value.
69///
70/// Component values retain their concrete Rust type and can be recovered with
71/// [`Self::downcast_ref`].
72pub struct ComponentData {
73    value: Box<dyn Component>,
74}
75
76impl ComponentData {
77    /// Erases a typed component value.
78    #[must_use]
79    pub fn new(value: impl Component) -> Self {
80        Self {
81            value: Box::new(value),
82        }
83    }
84
85    /// Returns the concrete value when it has type `T`.
86    #[must_use]
87    pub fn downcast_ref<T: DowncastType>(&self) -> Option<&T> {
88        self.value.downcast_ref::<T>()
89    }
90
91    /// Returns the concrete type key.
92    #[must_use]
93    pub fn type_key(&self) -> DowncastTypeKey {
94        self.value.downcast_type_key()
95    }
96}
97
98impl Clone for ComponentData {
99    fn clone(&self) -> Self {
100        Self {
101            value: self.value.clone_component(),
102        }
103    }
104}
105
106impl Debug for ComponentData {
107    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
108        formatter
109            .debug_tuple("ComponentData")
110            .field(&self.value)
111            .finish()
112    }
113}
114
115impl PartialEq for ComponentData {
116    fn eq(&self, other: &Self) -> bool {
117        self.value.component_eq(other.value.as_ref())
118    }
119}
120
121macro_rules! impl_component_downcast_type {
122    ($type:ty, $key:literal) => {
123        // SAFETY: This Steel-owned key uniquely identifies the concrete
124        // component implementation within the process.
125        unsafe impl DowncastType for $type {
126            const TYPE_KEY: DowncastTypeKey = DowncastTypeKey::new($key);
127        }
128    };
129}
130
131impl_component_downcast_type!(DamageTypeComponent, "steel:item_component/damage_type");
132impl_component_downcast_type!(
133    AdventureModePredicate,
134    "steel:item_component/adventure_mode_predicate"
135);
136impl_component_downcast_type!(LockCode, "steel:item_component/lock");
137impl_component_downcast_type!(CustomData, "steel:item_component/custom_data");
138impl_component_downcast_type!(CustomModelData, "steel:item_component/custom_model_data");
139impl_component_downcast_type!(DyeColor, "steel:dye_color");
140impl_component_downcast_type!(FoxVariant, "steel:fox_variant");
141impl_component_downcast_type!(SalmonVariant, "steel:salmon_variant");
142impl_component_downcast_type!(ParrotVariant, "steel:parrot_variant");
143impl_component_downcast_type!(TropicalFishPattern, "steel:tropical_fish_pattern");
144impl_component_downcast_type!(MooshroomVariant, "steel:mooshroom_variant");
145impl_component_downcast_type!(RabbitVariant, "steel:rabbit_variant");
146impl_component_downcast_type!(HorseVariant, "steel:horse_variant");
147impl_component_downcast_type!(LlamaVariant, "steel:llama_variant");
148impl_component_downcast_type!(AxolotlVariant, "steel:axolotl_variant");
149impl_component_downcast_type!(DyedItemColor, "steel:item_component/dyed_item_color");
150impl_component_downcast_type!(MapItemColor, "steel:item_component/map_item_color");
151impl_component_downcast_type!(MapId, "steel:item_component/map_id");
152impl_component_downcast_type!(FoodProperties, "steel:item_component/food");
153impl_component_downcast_type!(
154    SuspiciousStewEffects,
155    "steel:item_component/suspicious_stew_effects"
156);
157impl_component_downcast_type!(
158    WritableBookContent,
159    "steel:item_component/writable_book_content"
160);
161impl_component_downcast_type!(
162    WrittenBookContent,
163    "steel:item_component/written_book_content"
164);
165impl_component_downcast_type!(DebugStickState, "steel:item_component/debug_stick_state");
166impl_component_downcast_type!(Bees, "steel:item_component/bees");
167impl_component_downcast_type!(EntityData, "steel:item_component/entity_data");
168impl_component_downcast_type!(BlockEntityData, "steel:item_component/block_entity_data");
169impl_component_downcast_type!(KineticWeapon, "steel:item_component/kinetic_weapon");
170impl_component_downcast_type!(LodestoneTracker, "steel:item_component/lodestone_tracker");
171impl_component_downcast_type!(MapDecorations, "steel:item_component/map_decorations");
172impl_component_downcast_type!(FireworkExplosion, "steel:item_component/firework_explosion");
173impl_component_downcast_type!(Fireworks, "steel:item_component/fireworks");
174impl_component_downcast_type!(BlockItemStateProperties, "steel:item_component/block_state");
175impl_component_downcast_type!(BlocksAttacks, "steel:item_component/blocks_attacks");
176impl_component_downcast_type!(Consumable, "steel:item_component/consumable");
177impl_component_downcast_type!(DeathProtection, "steel:item_component/death_protection");
178impl_component_downcast_type!(ResolvableProfile, "steel:item_component/profile");
179impl_component_downcast_type!(SeededContainerLoot, "steel:item_component/container_loot");
180impl_component_downcast_type!(
181    OminousBottleAmplifier,
182    "steel:item_component/ominous_bottle_amplifier"
183);
184impl_component_downcast_type!(Enchantable, "steel:item_component/enchantable");
185impl_component_downcast_type!(InstrumentComponent, "steel:item_component/instrument");
186impl_component_downcast_type!(ArmorTrim, "steel:item_component/trim");
187impl_component_downcast_type!(Recipes, "steel:item_component/recipes");
188impl_component_downcast_type!(PotDecorations, "steel:item_component/pot_decorations");
189impl_component_downcast_type!(PotionContents, "steel:item_component/potion_contents");
190impl_component_downcast_type!(UseRemainder, "steel:item_component/use_remainder");
191impl_component_downcast_type!(
192    ChargedProjectiles,
193    "steel:item_component/charged_projectiles"
194);
195impl_component_downcast_type!(BundleContents, "steel:item_component/bundle_contents");
196impl_component_downcast_type!(ItemContainerContents, "steel:item_component/container");
197impl_component_downcast_type!(
198    SulfurCubeContent,
199    "steel:item_component/sulfur_cube_content"
200);
201impl_component_downcast_type!(BannerPatternLayers, "steel:item_component/banner_patterns");
202impl_component_downcast_type!(
203    PaintingVariantComponent,
204    "steel:item_component/painting_variant"
205);
206impl_component_downcast_type!(
207    ProvidesTrimMaterial,
208    "steel:item_component/provides_trim_material"
209);
210impl_component_downcast_type!(JukeboxPlayable, "steel:item_component/jukebox_playable");
211impl_component_downcast_type!(
212    ProvidesBannerPatterns,
213    "steel:item_component/provides_banner_patterns"
214);
215impl_component_downcast_type!(DamageResistant, "steel:item_component/damage_resistant");
216impl_component_downcast_type!(Repairable, "steel:item_component/repairable");
217impl_component_downcast_type!(Tool, "steel:item_component/tool");
218impl_component_downcast_type!(Weapon, "steel:item_component/weapon");
219impl_component_downcast_type!(AttackRange, "steel:item_component/attack_range");
220impl_component_downcast_type!(UseCooldown, "steel:item_component/use_cooldown");
221impl_component_downcast_type!(UseEffects, "steel:item_component/use_effects");
222impl_component_downcast_type!(ItemLore, "steel:item_component/lore");
223impl_component_downcast_type!(Rarity, "steel:item_component/rarity");
224impl_component_downcast_type!(TooltipDisplay, "steel:item_component/tooltip_display");
225impl_component_downcast_type!(SwingAnimation, "steel:item_component/swing_animation");
226impl_component_downcast_type!(
227    MapPostProcessing,
228    "steel:item_component/map_post_processing"
229);
230impl_component_downcast_type!(PiercingWeapon, "steel:item_component/piercing_weapon");
231impl_component_downcast_type!(Equippable, "steel:item_component/equippable");
232impl_component_downcast_type!(
233    ItemAttributeModifiers,
234    "steel:item_component/attribute_modifiers"
235);
236impl_component_downcast_type!(ItemEnchantments, "steel:item_component/enchantments");
237impl_component_downcast_type!(
238    RegistryReference<VillagerType>,
239    "steel:item_component/villager_variant"
240);
241impl_component_downcast_type!(
242    RegistryReference<WolfVariant>,
243    "steel:item_component/wolf_variant"
244);
245impl_component_downcast_type!(
246    RegistryReference<WolfSoundVariant>,
247    "steel:item_component/wolf_sound_variant"
248);
249impl_component_downcast_type!(
250    RegistryReference<PigVariant>,
251    "steel:item_component/pig_variant"
252);
253impl_component_downcast_type!(
254    RegistryReference<PigSoundVariant>,
255    "steel:item_component/pig_sound_variant"
256);
257impl_component_downcast_type!(
258    RegistryReference<CowVariant>,
259    "steel:item_component/cow_variant"
260);
261impl_component_downcast_type!(
262    RegistryReference<CowSoundVariant>,
263    "steel:item_component/cow_sound_variant"
264);
265impl_component_downcast_type!(
266    RegistryReference<ChickenVariant>,
267    "steel:item_component/chicken_variant"
268);
269impl_component_downcast_type!(
270    RegistryReference<ChickenSoundVariant>,
271    "steel:item_component/chicken_sound_variant"
272);
273impl_component_downcast_type!(
274    RegistryReference<ZombieNautilusVariant>,
275    "steel:item_component/zombie_nautilus_variant"
276);
277impl_component_downcast_type!(
278    RegistryReference<FrogVariant>,
279    "steel:item_component/frog_variant"
280);
281impl_component_downcast_type!(
282    RegistryReference<CatVariant>,
283    "steel:item_component/cat_variant"
284);
285impl_component_downcast_type!(
286    RegistryReference<CatSoundVariant>,
287    "steel:item_component/cat_sound_variant"
288);
289
290#[cfg(test)]
291mod tests {
292    use super::ComponentData;
293
294    #[test]
295    fn typed_values_downcast_by_deterministic_key() {
296        let value = ComponentData::new(17_i32);
297
298        assert_eq!(value.downcast_ref::<i32>(), Some(&17));
299        assert_eq!(value.downcast_ref::<bool>(), None);
300    }
301
302    #[test]
303    fn equality_requires_the_same_concrete_type() {
304        assert_eq!(ComponentData::new(17_i32), ComponentData::new(17_i32));
305        assert_ne!(ComponentData::new(17_i32), ComponentData::new(17.0_f32));
306    }
307}