Skip to main content

cu29_runtime/
reflect.rs

1//! Runtime reflection helpers built on top of `bevy_reflect`.
2
3#[cfg(feature = "reflect")]
4use alloc::format;
5use alloc::string::String;
6#[cfg(feature = "reflect")]
7use alloc::vec::Vec;
8
9#[cfg(feature = "reflect")]
10pub use bevy_reflect::*;
11#[cfg(feature = "reflect")]
12pub use bevy_reflect::{
13    array::{Array, ArrayInfo},
14    enums::{Enum, EnumInfo, VariantInfo, VariantType},
15    list::{List, ListInfo},
16    map::{Map, MapInfo},
17    set::{Set, SetInfo},
18    structs::{Struct, StructInfo},
19    tuple::{Tuple, TupleInfo},
20    tuple_struct::{TupleStruct, TupleStructInfo},
21};
22
23#[cfg(feature = "reflect")]
24pub trait ReflectTypePath: TypePath {}
25
26#[cfg(feature = "reflect")]
27impl<T: TypePath> ReflectTypePath for T {}
28
29#[cfg(not(feature = "reflect"))]
30pub use cu29_reflect_derive::Reflect;
31
32#[cfg(not(feature = "reflect"))]
33pub trait Reflect: 'static {}
34
35#[cfg(not(feature = "reflect"))]
36impl<T: 'static> Reflect for T {}
37
38#[cfg(not(feature = "reflect"))]
39pub trait TypePath {
40    fn type_path() -> &'static str {
41        core::any::type_name::<Self>()
42    }
43
44    fn short_type_path() -> &'static str {
45        core::any::type_name::<Self>()
46    }
47
48    fn type_ident() -> Option<&'static str> {
49        None
50    }
51
52    fn crate_name() -> Option<&'static str> {
53        None
54    }
55
56    fn module_path() -> Option<&'static str> {
57        None
58    }
59}
60
61/// Returns the canonical payload type path used by generated output metadata.
62#[doc(hidden)]
63#[inline]
64#[cfg(feature = "reflect")]
65pub fn __payload_type_path<T: TypePath>() -> &'static str {
66    T::type_path()
67}
68
69/// Returns the canonical payload type path used by generated output metadata.
70#[doc(hidden)]
71#[inline]
72#[cfg(not(feature = "reflect"))]
73pub fn __payload_type_path<T>() -> &'static str {
74    core::any::type_name::<T>()
75}
76
77#[cfg(not(feature = "reflect"))]
78macro_rules! impl_type_path_for_primitives {
79    ($($ty:ty),* $(,)?) => {
80        $(impl TypePath for $ty {})*
81    };
82}
83
84#[cfg(not(feature = "reflect"))]
85impl_type_path_for_primitives!(
86    (),
87    bool,
88    char,
89    str,
90    u8,
91    u16,
92    u32,
93    u64,
94    u128,
95    usize,
96    i8,
97    i16,
98    i32,
99    i64,
100    i128,
101    isize,
102    f32,
103    f64,
104);
105
106#[cfg(not(feature = "reflect"))]
107pub trait ReflectTypePath {}
108
109#[cfg(not(feature = "reflect"))]
110impl<T> ReflectTypePath for T {}
111
112#[cfg(not(feature = "reflect"))]
113pub trait GetTypeRegistration {}
114
115#[cfg(not(feature = "reflect"))]
116impl<T> GetTypeRegistration for T {}
117
118#[cfg(not(feature = "reflect"))]
119#[derive(Debug, Default, Clone, Copy)]
120pub struct TypeInfo;
121
122#[cfg(not(feature = "reflect"))]
123#[derive(Debug, Default)]
124pub struct TypeRegistry;
125
126#[cfg(not(feature = "reflect"))]
127impl TypeRegistry {
128    pub fn register<T>(&mut self) {
129        let _ = core::any::type_name::<T>();
130    }
131}
132
133/// Runtime task-reflect contract exposed by generated Copper applications.
134pub trait ReflectTaskIntrospection {
135    /// Returns a reflected immutable task instance for the given task id.
136    fn reflect_task(&self, task_id: &str) -> Option<&dyn Reflect>;
137
138    /// Returns a reflected mutable task instance for the given task id.
139    fn reflect_task_mut(&mut self, task_id: &str) -> Option<&mut dyn Reflect>;
140
141    /// Registers reflected schema types for this mission's app (tasks, messages, bridges).
142    fn register_reflect_types(_registry: &mut TypeRegistry) {}
143
144    /// Returns the reflected type path used as the task's debug-state schema.
145    fn debug_state_type_path(_task_id: &str) -> Option<&'static str> {
146        None
147    }
148
149    /// Borrows the task's current debug-state view.
150    fn with_debug_state<R>(&self, _task_id: &str, _f: impl FnOnce(&dyn Reflect) -> R) -> Option<R> {
151        None
152    }
153}
154
155/// Dumps a stable, human-readable schema snapshot for the registered reflected types.
156///
157/// This is intended for diagnostics, examples, and contract validation.
158#[cfg(feature = "reflect")]
159pub fn dump_type_registry_schema(registry: &TypeRegistry) -> String {
160    let mut entries: Vec<(&'static str, String)> = registry
161        .iter()
162        .map(|registration| {
163            let info = registration.type_info();
164            (info.type_path(), format!("{info:#?}"))
165        })
166        .collect();
167
168    entries.sort_by_key(|(left, _)| *left);
169
170    let mut dump = String::new();
171    for (type_path, info_dump) in entries {
172        dump.push_str("=== ");
173        dump.push_str(type_path);
174        dump.push_str(" ===\n");
175        dump.push_str(&info_dump);
176        dump.push('\n');
177    }
178    dump
179}
180
181#[cfg(not(feature = "reflect"))]
182pub fn dump_type_registry_schema(_registry: &TypeRegistry) -> String {
183    String::new()
184}
185
186#[cfg(all(test, not(feature = "reflect")))]
187mod tests {
188    use super::{__payload_type_path, TypePath};
189
190    struct PayloadWithoutTypePath;
191
192    fn assert_type_path<T: TypePath + ?Sized>() {}
193
194    #[test]
195    fn unit_and_primitive_type_paths_exist_without_reflect() {
196        assert_type_path::<()>();
197        assert_type_path::<i8>();
198    }
199
200    #[test]
201    fn payload_type_path_does_not_require_type_path_without_reflect() {
202        assert_eq!(
203            __payload_type_path::<PayloadWithoutTypePath>(),
204            core::any::type_name::<PayloadWithoutTypePath>()
205        );
206    }
207}