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#[cfg(not(feature = "reflect"))]
62macro_rules! impl_type_path_for_primitives {
63    ($($ty:ty),* $(,)?) => {
64        $(impl TypePath for $ty {})*
65    };
66}
67
68#[cfg(not(feature = "reflect"))]
69impl_type_path_for_primitives!(
70    (),
71    bool,
72    char,
73    str,
74    u8,
75    u16,
76    u32,
77    u64,
78    u128,
79    usize,
80    i8,
81    i16,
82    i32,
83    i64,
84    i128,
85    isize,
86    f32,
87    f64,
88);
89
90#[cfg(not(feature = "reflect"))]
91pub trait ReflectTypePath {}
92
93#[cfg(not(feature = "reflect"))]
94impl<T> ReflectTypePath for T {}
95
96#[cfg(not(feature = "reflect"))]
97pub trait GetTypeRegistration {}
98
99#[cfg(not(feature = "reflect"))]
100impl<T> GetTypeRegistration for T {}
101
102#[cfg(not(feature = "reflect"))]
103#[derive(Debug, Default, Clone, Copy)]
104pub struct TypeInfo;
105
106#[cfg(not(feature = "reflect"))]
107#[derive(Debug, Default)]
108pub struct TypeRegistry;
109
110#[cfg(not(feature = "reflect"))]
111impl TypeRegistry {
112    pub fn register<T>(&mut self) {
113        let _ = core::any::type_name::<T>();
114    }
115}
116
117/// Runtime task-reflect contract exposed by generated Copper applications.
118pub trait ReflectTaskIntrospection {
119    /// Returns a reflected immutable task instance for the given task id.
120    fn reflect_task(&self, task_id: &str) -> Option<&dyn Reflect>;
121
122    /// Returns a reflected mutable task instance for the given task id.
123    fn reflect_task_mut(&mut self, task_id: &str) -> Option<&mut dyn Reflect>;
124
125    /// Registers reflected schema types for this mission's app (tasks, messages, bridges).
126    fn register_reflect_types(_registry: &mut TypeRegistry) {}
127
128    /// Returns the reflected type path used as the task's debug-state schema.
129    fn debug_state_type_path(_task_id: &str) -> Option<&'static str> {
130        None
131    }
132
133    /// Borrows the task's current debug-state view.
134    fn with_debug_state<R>(&self, _task_id: &str, _f: impl FnOnce(&dyn Reflect) -> R) -> Option<R> {
135        None
136    }
137}
138
139/// Dumps a stable, human-readable schema snapshot for the registered reflected types.
140///
141/// This is intended for diagnostics, examples, and contract validation.
142#[cfg(feature = "reflect")]
143pub fn dump_type_registry_schema(registry: &TypeRegistry) -> String {
144    let mut entries: Vec<(&'static str, String)> = registry
145        .iter()
146        .map(|registration| {
147            let info = registration.type_info();
148            (info.type_path(), format!("{info:#?}"))
149        })
150        .collect();
151
152    entries.sort_by_key(|(left, _)| *left);
153
154    let mut dump = String::new();
155    for (type_path, info_dump) in entries {
156        dump.push_str("=== ");
157        dump.push_str(type_path);
158        dump.push_str(" ===\n");
159        dump.push_str(&info_dump);
160        dump.push('\n');
161    }
162    dump
163}
164
165#[cfg(not(feature = "reflect"))]
166pub fn dump_type_registry_schema(_registry: &TypeRegistry) -> String {
167    String::new()
168}
169
170#[cfg(all(test, not(feature = "reflect")))]
171mod tests {
172    use super::TypePath;
173
174    fn assert_type_path<T: TypePath + ?Sized>() {}
175
176    #[test]
177    fn unit_and_primitive_type_paths_exist_without_reflect() {
178        assert_type_path::<()>();
179        assert_type_path::<i8>();
180    }
181}