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#[derive(Clone, Default, Debug)]
26pub struct Generics(Option<Box<[GenericInfo]>>);
27
28impl Generics {
29 pub fn new() -> Self {
31 Self(None)
32 }
33
34 pub fn get_named(&self, name: &str) -> Option<&GenericInfo> {
38 self.0.iter().flatten().find(|info| info.name() == name)
41 }
42
43 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#[derive(Clone, Debug, From)]
71pub enum GenericInfo {
72 Type(TypeParamInfo),
76 Const(ConstParamInfo),
80}
81
82impl GenericInfo {
83 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 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#[derive(Clone, Debug)]
111pub struct TypeParamInfo {
112 name: Cow<'static, str>,
113 ty: Type,
114 default: Option<Type>,
115}
116
117impl TypeParamInfo {
118 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 pub fn with_default<T: TypePath + ?Sized>(mut self) -> Self {
129 self.default = Some(Type::of::<T>());
130 self
131 }
132
133 pub fn name(&self) -> &Cow<'static, str> {
135 &self.name
136 }
137
138 pub fn default(&self) -> Option<&Type> {
157 self.default.as_ref()
158 }
159
160 impl_type_methods!(ty);
161}
162
163#[derive(Clone, Debug)]
167pub struct ConstParamInfo {
168 name: Cow<'static, str>,
169 ty: Type,
170 default: Option<Arc<dyn Reflect>>,
173}
174
175impl ConstParamInfo {
176 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 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 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 pub fn name(&self) -> &Cow<'static, str> {
205 &self.name
206 }
207
208 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 ($field:ident) => {
236 $crate::generics::impl_generic_info_methods!(self => &self.$field);
237
238 pub fn with_generics(mut self, generics: crate::generics::Generics) -> Self {
240 self.$field = generics;
241 self
242 }
243 };
244 ($self:ident => $expr:expr) => {
246 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}