Skip to main content

aphyn_reflection/
lib.rs

1//! # aphyn_reflection
2//!
3//! Runtime reflection for engine types. It lets code inspect a type's
4//! structure, build and take apart values dynamically, and convert values to a
5//! self-describing data tree without knowing the concrete type at compile time.
6//! This is the groundwork for tooling — editors, inspectors, serializers — that
7//! must operate over arbitrary user types.
8//!
9//! # How to use
10//!
11//! The crate exposes three independent facilities. Pick the one that matches the
12//! task: a self-describing value tree ([`ReflRepr`]), dynamic construction and
13//! field access ([`ReflMapStruct`]), or a static structural schema ([`ReflTy`]).
14//!
15//! #### Convert a value to a self-describing representation
16//!
17//! Turn a typed value into a [`ReflRepr`] tree that mirrors its fields. Register
18//! the built-in representers for primitives, `Option`, and `Vec`, then add a
19//! [`ReflRepresenter`] for your own type and call
20//! [`into_repr`](ReflRepresenterRegistry::into_repr).
21//!
22//! ```
23//! use aphyn_reflection::*;
24//!
25//! struct Player {
26//!     health: u32,
27//!     name: String,
28//! }
29//!
30//! struct PlayerRepresenter;
31//! impl ReflRepresenter for PlayerRepresenter {
32//!     type Item = Player;
33//!
34//!     fn item_name(&self, _ctx: &ReflRepresenterCtx) -> String {
35//!         String::from("Player")
36//!     }
37//!
38//!     fn into_repr(&self, ctx: &ReflRepresenterCtx, v: &Self::Item) -> Option<ReflRepr> {
39//!         Some(ReflRepr::Struct(ReflReprStruct {
40//!             name: self.item_name(ctx),
41//!             fields: ReflReprFields::Named(vec![
42//!                 (String::from("health"), ctx.into_repr(&v.health)?),
43//!                 (String::from("name"), ctx.into_repr(&v.name)?),
44//!             ]),
45//!         }))
46//!     }
47//! }
48//!
49//! let mut reg = ReflRepresenterRegistry::default();
50//! set_common_representers(&mut reg);
51//! reg.set(Box::new(PlayerRepresenter));
52//!
53//! let player = Player { health: 100, name: String::from("Barry") };
54//! let repr = reg.into_repr(&player).unwrap();
55//!
56//! assert!(matches!(repr, ReflRepr::Struct(_)));
57//! ```
58//!
59//! #### Construct and destructure a value dynamically
60//!
61//! Build a value from type-erased parameters and read its fields back out
62//! without naming the concrete type, using [`ReflMapStruct`] and
63//! [`ReflMapStructField`].
64//!
65//! ```
66//! use aphyn_reflection::*;
67//! use std::collections::HashMap;
68//!
69//! struct Player {
70//!     health: u32,
71//!     name: String,
72//! }
73//!
74//! let map = ReflMapStruct::new(
75//!     |(health, name)| Player { health, name },
76//!     |v| (v.health, v.name),
77//!     HashMap::from([
78//!         ("health", ReflMapStructField::new(
79//!             |p: &Player| &p.health,
80//!             |p: &mut Player| &mut p.health,
81//!         )),
82//!         ("name", ReflMapStructField::new(
83//!             |p: &Player| &p.name,
84//!             |p: &mut Player| &mut p.name,
85//!         )),
86//!     ]),
87//! );
88//!
89//! let value = map.create(vec![Box::new(100_u32), Box::new(String::from("Barry"))]).unwrap();
90//! let health = map.fields["health"].get_ref(&*value).unwrap().downcast_ref::<u32>().unwrap();
91//!
92//! assert_eq!(*health, 100);
93//! ```
94//!
95//! #### Derive a type's structural schema
96//!
97//! Generate a [`ReflTy`] describing a type's fields and their type ids with the
98//! [`ReflectTy`](derive@ReflectTy) derive. The schema carries no values — only
99//! the shape.
100//!
101//! ```
102//! use aphyn_reflection::*;
103//!
104//! #[derive(ReflectTy)]
105//! struct Player {
106//!     health: u32,
107//!     name: String,
108//! }
109//!
110//! let ty = Player::refl_ty();
111//!
112//! assert!(matches!(ty, ReflTy::Struct(ReflTyStruct::Named(_))));
113//! ```
114//!
115//! # How to maintain
116//!
117//! The crate is early and experimental — the source is dotted with `// todo`
118//! markers for enums, collections, and generics, and several `example` /
119//! `*_use_case` functions (in `refl_ty.rs`, `registry.rs`, and the
120//! `aphyn_reflection_example` binary) are scratch demonstrations rather than
121//! API. It currently offers three parallel, independent reflection facilities;
122//! the private `Reflect` trait in this file is an unfinished attempt to unify
123//! them and is not used yet.
124//!
125//! #### Structural schema: `ReflTy`
126//!
127//! [`ReflTy`] is a value-free description of a type's shape (struct, enum,
128//! array, slice). Types are identified by [`ReflTyId`], whose identity is
129//! currently the [`std::any::type_name`] string — a `// todo` flags this as not
130//! collision-proof or guaranteed stable across compilations. [`ReflTyRegistry`]
131//! maps ids to schemas. The [`ReflectTy`](derive@ReflectTy) derive macro (in the
132//! sibling `aphyn_reflection_macros` crate) builds the `refl_ty()` body by
133//! emitting source text as a string and parsing it; it panics on lifetimes and
134//! unions, and references `ReflTy`/`ReflTyStruct`/`ReflTyId` unqualified, so the
135//! derive only works where those names are in scope.
136//!
137//! #### Dynamic construction: `ReflMapStruct`
138//!
139//! [`ReflMapStruct`] stores the user's `fn(P) -> T` factory and `fn(T) -> P`
140//! deconstructor as type-erased `*const ()` pointers, alongside monomorphized
141//! wrapper functions that capture the concrete `T` and `P` at construction and
142//! transmute the raw pointer back to the real signature before calling it. The
143//! transmute is sound only because each raw pointer is ever invoked through its
144//! own matching wrapper. [`TupleFromVecOfAny`] bridges `Vec<Box<dyn Any>>` and
145//! tuples — a macro implements it for arities 0 through 16 — which is what lets
146//! the factory take a variadic parameter list. [`create`](ReflMapStruct::create)
147//! and [`deconstruct`](ReflMapStruct::deconstruct) return `None` on an
148//! arity or type mismatch, surfaced as a failed `try_into`/`downcast`. Field
149//! access works the same way through [`ReflMapStructField`]. Enums and
150//! collections are not yet supported.
151//!
152//! #### Value tree: `ReflRepr` and representers
153//!
154//! [`ReflRepr`] is a runtime value tree of structs, enums, and primitives with a
155//! hand-written [`Debug`] impl that prints Rust-like syntax. A
156//! [`ReflRepresenter`] converts a concrete type to and from [`ReflRepr`];
157//! [`ReflRepresenterRegistry`] keys them by [`std::any::TypeId`]. Because
158//! `dyn ReflRepresenter` is not [`Any`](std::any::Any), the registry stores each
159//! representer double-boxed as `Box<Box<dyn ReflRepresenter<Item = T>>>` erased
160//! to `Box<dyn Any>` and recovers it by downcasting to the inner box type.
161//! [`ReflRepresenterCtx`] lends the registry to a representer so it can recurse
162//! into field types via [`ctx.into_repr`](ReflRepresenterCtx::into_repr).
163//! [`from_repr`](ReflRepresenter::from_repr) defaults to `None`: the value→repr
164//! direction works, but repr→value round-tripping is unimplemented for most
165//! types.
166//!
167//! `refl_repr_reg_entries` seeds the built-ins via [`set_common_representers`]:
168//! a macro generates primitive representers (integers → `Int(i128)`, floats →
169//! `Float(f64)`, plus `char` and `String`), and for each item type
170//! `set_common_generic_*` also registers `Option<T>` and `Vec<T>` wrappers. Only
171//! one level of generic nesting is pre-registered, so e.g. `Vec<Vec<u8>>` is not
172//! representable out of the box.
173
174mod refl_map;
175mod refl_repr;
176mod refl_repr_reg_entries;
177mod refl_ty;
178mod registry;
179mod tuple_from_vec_of_any;
180
181pub use refl_map::*;
182pub use refl_repr::*;
183pub use refl_repr_reg_entries::*;
184pub use refl_ty::*;
185pub use registry::*;
186pub use tuple_from_vec_of_any::*;
187
188pub use aphyn_reflection_macros::*;
189
190pub trait ReflectTy: 'static {
191    fn refl_ty() -> ReflTy;
192}
193
194// todo
195trait Reflect: 'static {
196    fn refl_ty() -> ReflTy;
197    fn to_refl_repr(&self) -> ReflRepr;
198    fn from_refl_repr(repr: &ReflRepr) -> Self;
199}