bevy_reflect/enums/enum_trait.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
use crate::generics::impl_generic_info_methods;
use crate::{
attributes::{impl_custom_attribute_methods, CustomAttributes},
type_info::impl_type_methods,
DynamicEnum, Generics, PartialReflect, Type, TypePath, VariantInfo, VariantType,
};
use alloc::sync::Arc;
use bevy_utils::HashMap;
use core::slice::Iter;
/// A trait used to power [enum-like] operations via [reflection].
///
/// This allows enums to be processed and modified dynamically at runtime without
/// necessarily knowing the actual type.
/// Enums are much more complex than their struct counterparts.
/// As a result, users will need to be mindful of conventions, considerations,
/// and complications when working with this trait.
///
/// # Variants
///
/// An enum is a set of choices called _variants_.
/// An instance of an enum can only exist as one of these choices at any given time.
/// Consider Rust's [`Option<T>`]. It's an enum with two variants: [`None`] and [`Some`].
/// If you're `None`, you can't be `Some` and vice versa.
///
/// > ⚠️ __This is very important:__
/// > The [`Enum`] trait represents an enum _as one of its variants_.
/// > It does not represent the entire enum since that's not true to how enums work.
///
/// Variants come in a few [flavors](VariantType):
///
/// | Variant Type | Syntax |
/// | ------------ | ------------------------------ |
/// | Unit | `MyEnum::Foo` |
/// | Tuple | `MyEnum::Foo( i32, i32 )` |
/// | Struct | `MyEnum::Foo{ value: String }` |
///
/// As you can see, a unit variant contains no fields, while tuple and struct variants
/// can contain one or more fields.
/// The fields in a tuple variant is defined by their _order_ within the variant.
/// Index `0` represents the first field in the variant and so on.
/// Fields in struct variants (excluding tuple structs), on the other hand, are
/// represented by a _name_.
///
/// # Implementation
///
/// > 💡 This trait can be automatically implemented using [`#[derive(Reflect)]`](derive@crate::Reflect)
/// > on an enum definition.
///
/// Despite the fact that enums can represent multiple states, traits only exist in one state
/// and must be applied to the entire enum rather than a particular variant.
/// Because of this limitation, the [`Enum`] trait must not only _represent_ any of the
/// three variant types, but also define the _methods_ for all three as well.
///
/// What does this mean? It means that even though a unit variant contains no fields, a
/// representation of that variant using the [`Enum`] trait will still contain methods for
/// accessing fields!
/// Again, this is to account for _all three_ variant types.
///
/// We recommend using the built-in [`#[derive(Reflect)]`](derive@crate::Reflect) macro to automatically handle all the
/// implementation details for you.
/// However, if you _must_ implement this trait manually, there are a few things to keep in mind...
///
/// ## Field Order
///
/// While tuple variants identify their fields by the order in which they are defined, struct
/// variants identify fields by their name.
/// However, both should allow access to fields by their defined order.
///
/// The reason all fields, regardless of variant type, need to be accessible by their order is
/// due to field iteration.
/// We need a way to iterate through each field in a variant, and the easiest way of achieving
/// that is through the use of field order.
///
/// The derive macro adds proper struct variant handling for [`Enum::index_of`], [`Enum::name_at`]
/// and [`Enum::field_at[_mut]`](Enum::field_at) methods.
/// The first two methods are __required__ for all struct variant types.
/// By convention, implementors should also handle the last method as well, but this is not
/// a strict requirement.
///
/// ## Field Names
///
/// Implementors may choose to handle [`Enum::index_of`], [`Enum::name_at`], and
/// [`Enum::field[_mut]`](Enum::field) for tuple variants by considering stringified `usize`s to be
/// valid names (such as `"3"`).
/// This isn't wrong to do, but the convention set by the derive macro is that it isn't supported.
/// It's preferred that these strings be converted to their proper `usize` representations and
/// the [`Enum::field_at[_mut]`](Enum::field_at) methods be used instead.
///
/// [enum-like]: https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html
/// [reflection]: crate
/// [`None`]: Option<T>::None
/// [`Some`]: Option<T>::Some
/// [`Reflect`]: bevy_reflect_derive::Reflect
pub trait Enum: PartialReflect {
/// Returns a reference to the value of the field (in the current variant) with the given name.
///
/// For non-[`VariantType::Struct`] variants, this should return `None`.
fn field(&self, name: &str) -> Option<&dyn PartialReflect>;
/// Returns a reference to the value of the field (in the current variant) at the given index.
fn field_at(&self, index: usize) -> Option<&dyn PartialReflect>;
/// Returns a mutable reference to the value of the field (in the current variant) with the given name.
///
/// For non-[`VariantType::Struct`] variants, this should return `None`.
fn field_mut(&mut self, name: &str) -> Option<&mut dyn PartialReflect>;
/// Returns a mutable reference to the value of the field (in the current variant) at the given index.
fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn PartialReflect>;
/// Returns the index of the field (in the current variant) with the given name.
///
/// For non-[`VariantType::Struct`] variants, this should return `None`.
fn index_of(&self, name: &str) -> Option<usize>;
/// Returns the name of the field (in the current variant) with the given index.
///
/// For non-[`VariantType::Struct`] variants, this should return `None`.
fn name_at(&self, index: usize) -> Option<&str>;
/// Returns an iterator over the values of the current variant's fields.
fn iter_fields(&self) -> VariantFieldIter;
/// Returns the number of fields in the current variant.
fn field_len(&self) -> usize;
/// The name of the current variant.
fn variant_name(&self) -> &str;
/// The index of the current variant.
fn variant_index(&self) -> usize;
/// The type of the current variant.
fn variant_type(&self) -> VariantType;
// Clones the enum into a [`DynamicEnum`].
fn clone_dynamic(&self) -> DynamicEnum;
/// Returns true if the current variant's type matches the given one.
fn is_variant(&self, variant_type: VariantType) -> bool {
self.variant_type() == variant_type
}
/// Returns the full path to the current variant.
fn variant_path(&self) -> String {
format!("{}::{}", self.reflect_type_path(), self.variant_name())
}
/// Will return `None` if [`TypeInfo`] is not available.
///
/// [`TypeInfo`]: crate::TypeInfo
fn get_represented_enum_info(&self) -> Option<&'static EnumInfo> {
self.get_represented_type_info()?.as_enum().ok()
}
}
/// A container for compile-time enum info, used by [`TypeInfo`](crate::TypeInfo).
#[derive(Clone, Debug)]
pub struct EnumInfo {
ty: Type,
generics: Generics,
variants: Box<[VariantInfo]>,
variant_names: Box<[&'static str]>,
variant_indices: HashMap<&'static str, usize>,
custom_attributes: Arc<CustomAttributes>,
#[cfg(feature = "documentation")]
docs: Option<&'static str>,
}
impl EnumInfo {
/// Create a new [`EnumInfo`].
///
/// # Arguments
///
/// * `variants`: The variants of this enum in the order they are defined
pub fn new<TEnum: Enum + TypePath>(variants: &[VariantInfo]) -> Self {
let variant_indices = variants
.iter()
.enumerate()
.map(|(index, variant)| (variant.name(), index))
.collect::<HashMap<_, _>>();
let variant_names = variants.iter().map(VariantInfo::name).collect();
Self {
ty: Type::of::<TEnum>(),
generics: Generics::new(),
variants: variants.to_vec().into_boxed_slice(),
variant_names,
variant_indices,
custom_attributes: Arc::new(CustomAttributes::default()),
#[cfg(feature = "documentation")]
docs: None,
}
}
/// Sets the docstring for this enum.
#[cfg(feature = "documentation")]
pub fn with_docs(self, docs: Option<&'static str>) -> Self {
Self { docs, ..self }
}
/// Sets the custom attributes for this enum.
pub fn with_custom_attributes(self, custom_attributes: CustomAttributes) -> Self {
Self {
custom_attributes: Arc::new(custom_attributes),
..self
}
}
/// A slice containing the names of all variants in order.
pub fn variant_names(&self) -> &[&'static str] {
&self.variant_names
}
/// Get a variant with the given name.
pub fn variant(&self, name: &str) -> Option<&VariantInfo> {
self.variant_indices
.get(name)
.map(|index| &self.variants[*index])
}
/// Get a variant at the given index.
pub fn variant_at(&self, index: usize) -> Option<&VariantInfo> {
self.variants.get(index)
}
/// Get the index of the variant with the given name.
pub fn index_of(&self, name: &str) -> Option<usize> {
self.variant_indices.get(name).copied()
}
/// Returns the full path to the given variant.
///
/// This does _not_ check if the given variant exists.
pub fn variant_path(&self, name: &str) -> String {
format!("{}::{name}", self.type_path())
}
/// Checks if a variant with the given name exists within this enum.
pub fn contains_variant(&self, name: &str) -> bool {
self.variant_indices.contains_key(name)
}
/// Iterate over the variants of this enum.
pub fn iter(&self) -> Iter<'_, VariantInfo> {
self.variants.iter()
}
/// The number of variants in this enum.
pub fn variant_len(&self) -> usize {
self.variants.len()
}
impl_type_methods!(ty);
/// The docstring of this enum, if any.
#[cfg(feature = "documentation")]
pub fn docs(&self) -> Option<&'static str> {
self.docs
}
impl_custom_attribute_methods!(self.custom_attributes, "enum");
impl_generic_info_methods!(generics);
}
/// An iterator over the fields in the current enum variant.
pub struct VariantFieldIter<'a> {
container: &'a dyn Enum,
index: usize,
}
impl<'a> VariantFieldIter<'a> {
pub fn new(container: &'a dyn Enum) -> Self {
Self {
container,
index: 0,
}
}
}
impl<'a> Iterator for VariantFieldIter<'a> {
type Item = VariantField<'a>;
fn next(&mut self) -> Option<Self::Item> {
let value = match self.container.variant_type() {
VariantType::Unit => None,
VariantType::Tuple => Some(VariantField::Tuple(self.container.field_at(self.index)?)),
VariantType::Struct => {
let name = self.container.name_at(self.index)?;
Some(VariantField::Struct(name, self.container.field(name)?))
}
};
self.index += value.is_some() as usize;
value
}
fn size_hint(&self) -> (usize, Option<usize>) {
let size = self.container.field_len();
(size, Some(size))
}
}
impl<'a> ExactSizeIterator for VariantFieldIter<'a> {}
pub enum VariantField<'a> {
Struct(&'a str, &'a dyn PartialReflect),
Tuple(&'a dyn PartialReflect),
}
impl<'a> VariantField<'a> {
pub fn name(&self) -> Option<&'a str> {
if let Self::Struct(name, ..) = self {
Some(*name)
} else {
None
}
}
pub fn value(&self) -> &'a dyn PartialReflect {
match *self {
Self::Struct(_, value) | Self::Tuple(value) => value,
}
}
}
// Tests that need access to internal fields have to go here rather than in mod.rs
#[cfg(test)]
mod tests {
use crate as bevy_reflect;
use crate::*;
#[derive(Reflect, Debug, PartialEq)]
enum MyEnum {
A,
B(usize, i32),
C { foo: f32, bar: bool },
}
#[test]
fn next_index_increment() {
// unit enums always return none, so index should stay at 0
let unit_enum = MyEnum::A;
let mut iter = unit_enum.iter_fields();
let size = iter.len();
for _ in 0..2 {
assert!(iter.next().is_none());
assert_eq!(size, iter.index);
}
// tuple enums we iter over each value (unnamed fields), stop after that
let tuple_enum = MyEnum::B(0, 1);
let mut iter = tuple_enum.iter_fields();
let size = iter.len();
for _ in 0..2 {
let prev_index = iter.index;
assert!(iter.next().is_some());
assert_eq!(prev_index, iter.index - 1);
}
for _ in 0..2 {
assert!(iter.next().is_none());
assert_eq!(size, iter.index);
}
// struct enums, we iterate over each field in the struct
let struct_enum = MyEnum::C {
foo: 0.,
bar: false,
};
let mut iter = struct_enum.iter_fields();
let size = iter.len();
for _ in 0..2 {
let prev_index = iter.index;
assert!(iter.next().is_some());
assert_eq!(prev_index, iter.index - 1);
}
for _ in 0..2 {
assert!(iter.next().is_none());
assert_eq!(size, iter.index);
}
}
}