Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_reflect/src/generics.rs
6598 views
1
use crate::type_info::impl_type_methods;
2
use crate::{Reflect, Type, TypePath};
3
use alloc::{borrow::Cow, boxed::Box};
4
use bevy_platform::sync::Arc;
5
use core::ops::Deref;
6
use derive_more::derive::From;
7
8
/// The generic parameters of a type.
9
///
10
/// This is automatically generated via the [`Reflect` derive macro]
11
/// and stored on the [`TypeInfo`] returned by [`Typed::type_info`]
12
/// for types that have generics.
13
///
14
/// It supports both type parameters and const parameters
15
/// so long as they implement [`TypePath`].
16
///
17
/// If the type has no generics, this will be empty.
18
///
19
/// If the type is marked with `#[reflect(type_path = false)]`,
20
/// the generics will be empty even if the type has generics.
21
///
22
/// [`Reflect` derive macro]: bevy_reflect_derive::Reflect
23
/// [`TypeInfo`]: crate::type_info::TypeInfo
24
/// [`Typed::type_info`]: crate::Typed::type_info
25
#[derive(Clone, Default, Debug)]
26
pub struct Generics(Box<[GenericInfo]>);
27
28
impl Generics {
29
/// Creates an empty set of generics.
30
pub fn new() -> Self {
31
Self(Box::new([]))
32
}
33
34
/// Finds the generic parameter with the given name.
35
///
36
/// Returns `None` if no such parameter exists.
37
pub fn get_named(&self, name: &str) -> Option<&GenericInfo> {
38
// For small sets of generics (the most common case),
39
// a linear search is often faster using a `HashMap`.
40
self.0.iter().find(|info| info.name() == name)
41
}
42
43
/// Adds the given generic parameter to the set.
44
pub fn with(mut self, info: impl Into<GenericInfo>) -> Self {
45
self.0 = IntoIterator::into_iter(self.0)
46
.chain(core::iter::once(info.into()))
47
.collect();
48
self
49
}
50
}
51
52
impl<T: Into<GenericInfo>> FromIterator<T> for Generics {
53
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
54
Self(iter.into_iter().map(Into::into).collect())
55
}
56
}
57
58
impl Deref for Generics {
59
type Target = [GenericInfo];
60
61
fn deref(&self) -> &Self::Target {
62
&self.0
63
}
64
}
65
66
/// An enum representing a generic parameter.
67
#[derive(Clone, Debug, From)]
68
pub enum GenericInfo {
69
/// A type parameter.
70
///
71
/// An example would be `T` in `struct Foo<T, U>`.
72
Type(TypeParamInfo),
73
/// A const parameter.
74
///
75
/// An example would be `N` in `struct Foo<const N: usize>`.
76
Const(ConstParamInfo),
77
}
78
79
impl GenericInfo {
80
/// The name of the generic parameter.
81
pub fn name(&self) -> &Cow<'static, str> {
82
match self {
83
Self::Type(info) => info.name(),
84
Self::Const(info) => info.name(),
85
}
86
}
87
88
/// Whether the generic parameter is a const parameter.
89
pub fn is_const(&self) -> bool {
90
match self {
91
Self::Type(_) => false,
92
Self::Const(_) => true,
93
}
94
}
95
96
impl_type_methods!(self => {
97
match self {
98
Self::Type(info) => info.ty(),
99
Self::Const(info) => info.ty(),
100
}
101
});
102
}
103
104
/// Type information for a generic type parameter.
105
///
106
/// An example of a type parameter would be `T` in `struct Foo<T>`.
107
#[derive(Clone, Debug)]
108
pub struct TypeParamInfo {
109
name: Cow<'static, str>,
110
ty: Type,
111
default: Option<Type>,
112
}
113
114
impl TypeParamInfo {
115
/// Creates a new type parameter with the given name.
116
pub fn new<T: TypePath + ?Sized>(name: impl Into<Cow<'static, str>>) -> Self {
117
Self {
118
name: name.into(),
119
ty: Type::of::<T>(),
120
default: None,
121
}
122
}
123
124
/// Sets the default type for the parameter.
125
pub fn with_default<T: TypePath + ?Sized>(mut self) -> Self {
126
self.default = Some(Type::of::<T>());
127
self
128
}
129
130
/// The name of the type parameter.
131
pub fn name(&self) -> &Cow<'static, str> {
132
&self.name
133
}
134
135
/// The default type for the parameter, if any.
136
///
137
/// # Example
138
///
139
/// ```
140
/// # use bevy_reflect::{GenericInfo, Reflect, Typed};
141
/// #[derive(Reflect)]
142
/// struct Foo<T = f32>(T);
143
///
144
/// let generics = Foo::<String>::type_info().generics();
145
/// let GenericInfo::Type(info) = generics.get_named("T").unwrap() else {
146
/// panic!("expected a type parameter");
147
/// };
148
///
149
/// let default = info.default().unwrap();
150
///
151
/// assert!(default.is::<f32>());
152
/// ```
153
pub fn default(&self) -> Option<&Type> {
154
self.default.as_ref()
155
}
156
157
impl_type_methods!(ty);
158
}
159
160
/// Type information for a const generic parameter.
161
///
162
/// An example of a const parameter would be `N` in `struct Foo<const N: usize>`.
163
#[derive(Clone, Debug)]
164
pub struct ConstParamInfo {
165
name: Cow<'static, str>,
166
ty: Type,
167
// Rust currently only allows certain primitive types in const generic position,
168
// meaning that `Reflect` is guaranteed to be implemented for the default value.
169
default: Option<Arc<dyn Reflect>>,
170
}
171
172
impl ConstParamInfo {
173
/// Creates a new const parameter with the given name.
174
pub fn new<T: TypePath + ?Sized>(name: impl Into<Cow<'static, str>>) -> Self {
175
Self {
176
name: name.into(),
177
ty: Type::of::<T>(),
178
default: None,
179
}
180
}
181
182
/// Sets the default value for the parameter.
183
pub fn with_default<T: Reflect + 'static>(mut self, default: T) -> Self {
184
let arc = Arc::new(default);
185
186
#[cfg(not(target_has_atomic = "ptr"))]
187
#[expect(
188
unsafe_code,
189
reason = "unsized coercion is an unstable feature for non-std types"
190
)]
191
// SAFETY:
192
// - Coercion from `T` to `dyn Reflect` is valid as `T: Reflect + 'static`
193
// - `Arc::from_raw` receives a valid pointer from a previous call to `Arc::into_raw`
194
let arc = unsafe { Arc::from_raw(Arc::into_raw(arc) as *const dyn Reflect) };
195
196
self.default = Some(arc);
197
self
198
}
199
200
/// The name of the const parameter.
201
pub fn name(&self) -> &Cow<'static, str> {
202
&self.name
203
}
204
205
/// The default value for the parameter, if any.
206
///
207
/// # Example
208
///
209
/// ```
210
/// # use bevy_reflect::{GenericInfo, Reflect, Typed};
211
/// #[derive(Reflect)]
212
/// struct Foo<const N: usize = 10>([u8; N]);
213
///
214
/// let generics = Foo::<5>::type_info().generics();
215
/// let GenericInfo::Const(info) = generics.get_named("N").unwrap() else {
216
/// panic!("expected a const parameter");
217
/// };
218
///
219
/// let default = info.default().unwrap();
220
///
221
/// assert_eq!(default.downcast_ref::<usize>().unwrap(), &10);
222
/// ```
223
pub fn default(&self) -> Option<&dyn Reflect> {
224
self.default.as_deref()
225
}
226
227
impl_type_methods!(ty);
228
}
229
230
macro_rules! impl_generic_info_methods {
231
// Implements both getter and setter methods for the given field.
232
($field:ident) => {
233
$crate::generics::impl_generic_info_methods!(self => &self.$field);
234
235
/// Sets the generic parameters for this type.
236
pub fn with_generics(mut self, generics: crate::generics::Generics) -> Self {
237
self.$field = generics;
238
self
239
}
240
};
241
// Implements only a getter method for the given expression.
242
($self:ident => $expr:expr) => {
243
/// Gets the generic parameters for this type.
244
pub fn generics(&$self) -> &crate::generics::Generics {
245
$expr
246
}
247
};
248
}
249
250
pub(crate) use impl_generic_info_methods;
251
252
#[cfg(test)]
253
mod tests {
254
use super::*;
255
use crate::{Reflect, Typed};
256
use alloc::string::String;
257
use core::fmt::Debug;
258
259
#[test]
260
fn should_maintain_order() {
261
#[derive(Reflect)]
262
struct Test<T, U: Debug, const N: usize>([(T, U); N]);
263
264
let generics = <Test<f32, String, 10> as Typed>::type_info()
265
.as_tuple_struct()
266
.unwrap()
267
.generics();
268
269
assert_eq!(generics.len(), 3);
270
271
let mut iter = generics.iter();
272
273
let t = iter.next().unwrap();
274
assert_eq!(t.name(), "T");
275
assert!(t.ty().is::<f32>());
276
assert!(!t.is_const());
277
278
let u = iter.next().unwrap();
279
assert_eq!(u.name(), "U");
280
assert!(u.ty().is::<String>());
281
assert!(!u.is_const());
282
283
let n = iter.next().unwrap();
284
assert_eq!(n.name(), "N");
285
assert!(n.ty().is::<usize>());
286
assert!(n.is_const());
287
288
assert!(iter.next().is_none());
289
}
290
291
#[test]
292
fn should_get_by_name() {
293
#[derive(Reflect)]
294
enum Test<T, U: Debug, const N: usize> {
295
Array([(T, U); N]),
296
}
297
298
let generics = <Test<f32, String, 10> as Typed>::type_info()
299
.as_enum()
300
.unwrap()
301
.generics();
302
303
let t = generics.get_named("T").unwrap();
304
assert_eq!(t.name(), "T");
305
assert!(t.ty().is::<f32>());
306
assert!(!t.is_const());
307
308
let u = generics.get_named("U").unwrap();
309
assert_eq!(u.name(), "U");
310
assert!(u.ty().is::<String>());
311
assert!(!u.is_const());
312
313
let n = generics.get_named("N").unwrap();
314
assert_eq!(n.name(), "N");
315
assert!(n.ty().is::<usize>());
316
assert!(n.is_const());
317
}
318
319
#[test]
320
fn should_store_defaults() {
321
#[derive(Reflect)]
322
struct Test<T, U: Debug = String, const N: usize = 10>([(T, U); N]);
323
324
let generics = <Test<f32> as Typed>::type_info()
325
.as_tuple_struct()
326
.unwrap()
327
.generics();
328
329
let GenericInfo::Type(u) = generics.get_named("U").unwrap() else {
330
panic!("expected a type parameter");
331
};
332
assert_eq!(u.default().unwrap(), &Type::of::<String>());
333
334
let GenericInfo::Const(n) = generics.get_named("N").unwrap() else {
335
panic!("expected a const parameter");
336
};
337
assert_eq!(n.default().unwrap().downcast_ref::<usize>().unwrap(), &10);
338
}
339
}
340
341