Skip to main content

bevy_reflect/
generics.rs

1use crate::ty::impl_type_methods;
2use crate::{Reflect, Type, TypePath};
3use alloc::{borrow::Cow, boxed::Box};
4use bevy_platform::sync::Arc;
5use core::ops::Deref;
6use 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::info::TypeInfo
24/// [`Typed::type_info`]: crate::Typed::type_info
25#[derive(Clone, Default, Debug)]
26pub struct Generics(Option<Box<[GenericInfo]>>);
27
28impl Generics {
29    /// Creates an empty set of generics.
30    pub fn new() -> Self {
31        Self(None)
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().flatten().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 = Some(match self.0 {
46            Some(existing) => IntoIterator::into_iter(existing)
47                .chain(core::iter::once(info.into()))
48                .collect(),
49            None => Box::new([info.into()]),
50        });
51        self
52    }
53}
54
55impl<T: Into<GenericInfo>> FromIterator<T> for Generics {
56    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
57        Self(Some(iter.into_iter().map(Into::into).collect()))
58    }
59}
60
61impl Deref for Generics {
62    type Target = [GenericInfo];
63
64    fn deref(&self) -> &Self::Target {
65        self.0.as_deref().unwrap_or(&[])
66    }
67}
68
69/// An enum representing a generic parameter.
70#[derive(Clone, Debug, From)]
71pub enum GenericInfo {
72    /// A type parameter.
73    ///
74    /// An example would be `T` in `struct Foo<T, U>`.
75    Type(TypeParamInfo),
76    /// A const parameter.
77    ///
78    /// An example would be `N` in `struct Foo<const N: usize>`.
79    Const(ConstParamInfo),
80}
81
82impl GenericInfo {
83    /// The name of the generic parameter.
84    pub fn name(&self) -> &Cow<'static, str> {
85        match self {
86            Self::Type(info) => info.name(),
87            Self::Const(info) => info.name(),
88        }
89    }
90
91    /// Whether the generic parameter is a const parameter.
92    pub fn is_const(&self) -> bool {
93        match self {
94            Self::Type(_) => false,
95            Self::Const(_) => true,
96        }
97    }
98
99    impl_type_methods!(self => {
100        match self {
101            Self::Type(info) => info.ty(),
102            Self::Const(info) => info.ty(),
103        }
104    });
105}
106
107/// Type information for a generic type parameter.
108///
109/// An example of a type parameter would be `T` in `struct Foo<T>`.
110#[derive(Clone, Debug)]
111pub struct TypeParamInfo {
112    name: Cow<'static, str>,
113    ty: Type,
114    default: Option<Type>,
115}
116
117impl TypeParamInfo {
118    /// Creates a new type parameter with the given name.
119    pub fn new<T: TypePath + ?Sized>(name: impl Into<Cow<'static, str>>) -> Self {
120        Self {
121            name: name.into(),
122            ty: Type::of::<T>(),
123            default: None,
124        }
125    }
126
127    /// Sets the default type for the parameter.
128    pub fn with_default<T: TypePath + ?Sized>(mut self) -> Self {
129        self.default = Some(Type::of::<T>());
130        self
131    }
132
133    /// The name of the type parameter.
134    pub fn name(&self) -> &Cow<'static, str> {
135        &self.name
136    }
137
138    /// The default type for the parameter, if any.
139    ///
140    /// # Example
141    ///
142    /// ```
143    /// # use bevy_reflect::{GenericInfo, Reflect, Typed};
144    /// #[derive(Reflect)]
145    /// struct Foo<T = f32>(T);
146    ///
147    /// let generics = Foo::<String>::type_info().generics();
148    /// let GenericInfo::Type(info) = generics.get_named("T").unwrap() else {
149    ///     panic!("expected a type parameter");
150    /// };
151    ///
152    /// let default = info.default().unwrap();
153    ///
154    /// assert!(default.is::<f32>());
155    /// ```
156    pub fn default(&self) -> Option<&Type> {
157        self.default.as_ref()
158    }
159
160    impl_type_methods!(ty);
161}
162
163/// Type information for a const generic parameter.
164///
165/// An example of a const parameter would be `N` in `struct Foo<const N: usize>`.
166#[derive(Clone, Debug)]
167pub struct ConstParamInfo {
168    name: Cow<'static, str>,
169    ty: Type,
170    // Rust currently only allows certain primitive types in const generic position,
171    // meaning that `Reflect` is guaranteed to be implemented for the default value.
172    default: Option<Arc<dyn Reflect>>,
173}
174
175impl ConstParamInfo {
176    /// Creates a new const parameter with the given name.
177    pub fn new<T: TypePath + ?Sized>(name: impl Into<Cow<'static, str>>) -> Self {
178        Self {
179            name: name.into(),
180            ty: Type::of::<T>(),
181            default: None,
182        }
183    }
184
185    /// Sets the default value for the parameter.
186    pub fn with_default<T: Reflect + 'static>(mut self, default: T) -> Self {
187        let arc = Arc::new(default);
188
189        #[cfg(not(target_has_atomic = "ptr"))]
190        #[expect(
191            unsafe_code,
192            reason = "unsized coercion is an unstable feature for non-std types"
193        )]
194        // SAFETY:
195        // - Coercion from `T` to `dyn Reflect` is valid as `T: Reflect + 'static`
196        // - `Arc::from_raw` receives a valid pointer from a previous call to `Arc::into_raw`
197        let arc = unsafe { Arc::from_raw(Arc::into_raw(arc) as *const dyn Reflect) };
198
199        self.default = Some(arc);
200        self
201    }
202
203    /// The name of the const parameter.
204    pub fn name(&self) -> &Cow<'static, str> {
205        &self.name
206    }
207
208    /// The default value for the parameter, if any.
209    ///
210    /// # Example
211    ///
212    /// ```
213    /// # use bevy_reflect::{GenericInfo, Reflect, Typed};
214    /// #[derive(Reflect)]
215    /// struct Foo<const N: usize = 10>([u8; N]);
216    ///
217    /// let generics = Foo::<5>::type_info().generics();
218    /// let GenericInfo::Const(info) = generics.get_named("N").unwrap() else {
219    ///    panic!("expected a const parameter");
220    /// };
221    ///
222    /// let default = info.default().unwrap();
223    ///
224    /// assert_eq!(default.downcast_ref::<usize>().unwrap(), &10);
225    /// ```
226    pub fn default(&self) -> Option<&dyn Reflect> {
227        self.default.as_deref()
228    }
229
230    impl_type_methods!(ty);
231}
232
233macro_rules! impl_generic_info_methods {
234    // Implements both getter and setter methods for the given field.
235    ($field:ident) => {
236        $crate::generics::impl_generic_info_methods!(self => &self.$field);
237
238        /// Sets the generic parameters for this type.
239        pub fn with_generics(mut self, generics: crate::generics::Generics) -> Self {
240            self.$field = generics;
241            self
242        }
243    };
244    // Implements only a getter method for the given expression.
245    ($self:ident => $expr:expr) => {
246        /// Gets the generic parameters for this type.
247        pub fn generics(&$self) -> &crate::generics::Generics {
248            $expr
249        }
250    };
251}
252
253pub(crate) use impl_generic_info_methods;
254
255#[cfg(test)]
256mod tests {
257    use super::*;
258    use crate::{Reflect, Typed};
259    use alloc::string::String;
260    use core::fmt::Debug;
261
262    #[test]
263    fn should_maintain_order() {
264        #[derive(Reflect)]
265        struct Test<T, U: Debug, const N: usize>([(T, U); N]);
266
267        let generics = <Test<f32, String, 10> as Typed>::type_info()
268            .as_tuple_struct()
269            .unwrap()
270            .generics();
271
272        assert_eq!(generics.len(), 3);
273
274        let mut iter = generics.iter();
275
276        let t = iter.next().unwrap();
277        assert_eq!(t.name(), "T");
278        assert!(t.ty().is::<f32>());
279        assert!(!t.is_const());
280
281        let u = iter.next().unwrap();
282        assert_eq!(u.name(), "U");
283        assert!(u.ty().is::<String>());
284        assert!(!u.is_const());
285
286        let n = iter.next().unwrap();
287        assert_eq!(n.name(), "N");
288        assert!(n.ty().is::<usize>());
289        assert!(n.is_const());
290
291        assert!(iter.next().is_none());
292    }
293
294    #[test]
295    fn should_get_by_name() {
296        #[derive(Reflect)]
297        enum Test<T, U: Debug, const N: usize> {
298            Array([(T, U); N]),
299        }
300
301        let generics = <Test<f32, String, 10> as Typed>::type_info()
302            .as_enum()
303            .unwrap()
304            .generics();
305
306        let t = generics.get_named("T").unwrap();
307        assert_eq!(t.name(), "T");
308        assert!(t.ty().is::<f32>());
309        assert!(!t.is_const());
310
311        let u = generics.get_named("U").unwrap();
312        assert_eq!(u.name(), "U");
313        assert!(u.ty().is::<String>());
314        assert!(!u.is_const());
315
316        let n = generics.get_named("N").unwrap();
317        assert_eq!(n.name(), "N");
318        assert!(n.ty().is::<usize>());
319        assert!(n.is_const());
320    }
321
322    #[test]
323    fn should_store_defaults() {
324        #[derive(Reflect)]
325        struct Test<T, U: Debug = String, const N: usize = 10>([(T, U); N]);
326
327        let generics = <Test<f32> as Typed>::type_info()
328            .as_tuple_struct()
329            .unwrap()
330            .generics();
331
332        let GenericInfo::Type(u) = generics.get_named("U").unwrap() else {
333            panic!("expected a type parameter");
334        };
335        assert_eq!(u.default().unwrap(), &Type::of::<String>());
336
337        let GenericInfo::Const(n) = generics.get_named("N").unwrap() else {
338            panic!("expected a const parameter");
339        };
340        assert_eq!(n.default().unwrap().downcast_ref::<usize>().unwrap(), &10);
341    }
342
343    #[test]
344    fn should_not_capture_generics_with_type_path_opt_out() {
345        #[derive(Reflect)]
346        #[reflect(type_path = false)]
347        struct Test<T: Default, const N: usize = 10>(#[reflect(ignore)] T);
348
349        impl<T: Default + 'static, const N: usize> TypePath for Test<T, N> {
350            fn type_path() -> &'static str {
351                ::core::any::type_name::<Self>()
352            }
353
354            fn short_type_path() -> &'static str {
355                "Test<T, N>"
356            }
357        }
358
359        let generics = <Test<f32> as Typed>::type_info()
360            .as_tuple_struct()
361            .unwrap()
362            .generics();
363
364        assert!(generics.is_empty());
365    }
366
367    #[test]
368    fn should_capture_generics_on_opaque_type() {
369        #[derive(Reflect, Clone)]
370        #[reflect(opaque)]
371        struct Test<T: Clone + Default, const N: usize = 10>(#[reflect(ignore)] T);
372
373        let generics = <Test<f32> as Typed>::type_info()
374            .as_opaque()
375            .unwrap()
376            .generics();
377
378        let t = generics.get_named("T").unwrap();
379        assert_eq!(t.name(), "T");
380        assert!(t.is::<f32>());
381        assert!(!t.is_const());
382
383        let n = generics.get_named("N").unwrap();
384        assert_eq!(n.name(), "N");
385        assert!(n.is::<usize>());
386        assert!(n.is_const());
387    }
388
389    #[test]
390    fn should_not_capture_generics_on_opaque_type_with_type_path_opt_out() {
391        #[derive(Reflect, Clone)]
392        #[reflect(opaque)]
393        #[reflect(type_path = false)]
394        struct Test<T: Clone + Default, const N: usize = 10>(#[reflect(ignore)] T);
395
396        impl<T: Clone + Default + 'static, const N: usize> TypePath for Test<T, N> {
397            fn type_path() -> &'static str {
398                ::core::any::type_name::<Self>()
399            }
400
401            fn short_type_path() -> &'static str {
402                "Test<T, N>"
403            }
404        }
405
406        let generics = <Test<f32> as Typed>::type_info()
407            .as_opaque()
408            .unwrap()
409            .generics();
410
411        assert!(generics.is_empty());
412    }
413}