reflectix_core/
lib.rs

1#![deny(missing_docs)]
2#![allow(missing_docs)]
3
4/// Information about type fields (if there is any)
5#[derive(Clone, PartialEq, Eq, Debug)]
6pub enum Fields {
7    /// Type is structure-like and has fields with names
8    Named(&'static [Field]),
9
10    /// Type is tuple-like and has fields that can be referred by their index (just like you would be accessing plain tuple)
11    ///
12    Indexed(&'static [Field]),
13
14    /// Type is unit and doesn't have any fields
15    Unit,
16}
17
18/// Information about data contained within type
19///
20/// [`Data::Primitive`] is special case for fundamental rust types.
21/// This crate assumes that your types will be built using those.
22#[derive(Clone, PartialEq, Eq, Debug)]
23pub enum Data {
24    /// Fundamental type, which doesn't have any fields. You **can't** define types with this kind of data
25    Primitive,
26    /// Struct-like, can be tuple struct or default struct
27    Struct(Fields),
28
29    /// Variants of this enum
30    Enum(Variants),
31
32    /// Unit type, which means that type doesn't have any fields.
33    ///
34    /// **Note**: that this differs from [`Data::Primitive`] semantic meaning: you can define types which hold this data
35    Unit,
36}
37
38/// Discriminant of particular field
39#[derive(Clone, PartialEq, Eq, Debug)]
40pub enum FieldId {
41    /// Index of field in tuple-like type
42    Index(usize),
43    /// Name of target field
44    Named(&'static str),
45}
46
47/// Field of type
48///
49/// Single structure for fields of tuple-like types and fields of structures
50#[derive(Clone, PartialEq, Eq, Debug)]
51pub struct Field {
52    /// Identifier of field inside related type
53    ///
54    /// Acts as path in filesystem
55    pub id: FieldId,
56    /// Associated info of field's type
57    pub ty: &'static Type,
58}
59impl From<&'static str> for FieldId {
60    fn from(s: &'static str) -> Self {
61        FieldId::Named(s)
62    }
63}
64
65impl From<usize> for FieldId {
66    fn from(i: usize) -> Self {
67        FieldId::Index(i)
68    }
69}
70
71
72/// Variant of enum type
73#[derive(Clone, PartialEq, Eq, Debug)]
74pub struct Variant {
75    #[allow(missing_docs)]
76    pub ident: &'static str,
77    #[allow(missing_docs)]
78    pub fields: Fields,
79}
80#[allow(missing_docs)]
81#[derive(Clone, PartialEq, Eq, Debug)]
82pub struct Variants {
83    #[allow(missing_docs)]
84    pub variants: &'static [Variant],
85}
86
87/// Information about type
88///
89/// if [`TypeInfo`] is implemented, comes as associated constant
90#[derive(Clone, PartialEq, Eq, Debug)]
91pub struct Type {
92    /// Type name, exactly as in code (case and underscores are preserved)
93    pub ident: &'static str,
94    /// Type of data that this type contains
95    pub data: Data,
96}
97
98/// If attempt to borrow field was incorrect
99#[derive(thiserror::Error, Debug)]
100pub enum FieldAccessError {
101    /// Requested type doesn't actually match that of the field
102    #[error("Requested type doesn't match actual type")]
103    UnmatchingType,
104
105    /// If there were an attempt to access field in unit type
106    #[error("Attempt to access field in unit type/variant")]
107    Unit,
108
109    /// If accessing field that is not present in type
110    #[error("Field not found")]
111    NotFound,
112}
113
114/// Failure of type construction
115#[derive(thiserror::Error, Debug)]
116pub enum RuntimeConstructError {
117    /// Attempted to construct primitive type
118    #[error("Can't construct primitive type")]
119    Primitive,
120
121    /// Invalid type was passed as argument to constructor
122    #[error("Invalid type at {index} was passed to runtime constructor")]
123    UnexpectedType {
124        #[allow(missing_docs)]
125        index: usize,
126        /// Name of expected type
127        expected: &'static str,
128    },
129
130    /// Related enum doesn't have requested variant
131    #[error("Requested variant doesn't exist")]
132    InvalidVariant,
133
134    #[error("Some fields of this type are not public")]
135    #[allow(missing_docs)]
136    PrivateFields,
137
138    #[error("Called `construct_struct` on enum type")]
139    #[allow(missing_docs)]
140    NotStruct,
141
142    #[error("Called `construct_enum` on a struct type")]
143    #[allow(missing_docs)]
144    NotEnum,
145
146    #[error("Not enough arguments were passed")]
147    #[allow(missing_docs)]
148    NotEnoughArgs,
149}
150
151/// Object-safe version of [`TypeInfo`]
152///
153/// Additionally provides ability to construct type (if it's not a enum without variants),
154/// and ability to borrow (both immutably and mutably) fields
155pub trait TypeInfoDynamic: std::any::Any {
156    /// Get [`Type`] information for this type
157    ///
158    /// Because it accepts reference to self, it can be called on [`dyn`] trait-objects
159    fn get_dynamic(&self) -> &'static Type;
160
161    /// Constructs this type if it is a struct
162    ///
163    /// Attempts to downcast passed arguments to type of fields.
164    /// Multiple fields can be of same type, just make sure that order is preserved or you might get unexpected results
165    ///
166    /// If called on enum type, [`RuntimeConstructError::NotStruct`] will be returned
167    ///
168    /// **Note**: Arguments must be passed in same order as definition order of fields inside struct
169    fn construct_struct(
170        &self,
171        args: Vec<Box<dyn Any>>,
172    ) -> Result<Box<dyn Any>, RuntimeConstructError>;
173
174    /// Constructs `Self` if it is enum
175    ///
176    /// Attempts to downcast passed arguments as type of fields of requested variant, if there are any.
177    /// List of required arguments is target variant-dependent, as well as their order
178    ///
179    /// If variant is unit, no arguments will be required aside from `variant`
180    ///
181    /// **Note**: Arguments must be passed in same order as definition order of fields inside of particular variant
182    fn construct_enum(
183        &self,
184        variant: &'static str, // some sort of safety gate, because in fully reflective usage one wouldn't be able to construct &'static variant name
185        args: Vec<Box<dyn Any>>,
186    ) -> Result<Box<dyn Any>, RuntimeConstructError>;
187
188    /// Borrow immutably field inside this type
189    ///
190    /// Type must not be a unit and `id` must be valid in terms of this type (present)
191    ///
192    /// Returns [`Unsizeable`], which can further be downcast to "nameable" type
193    fn field<'s>(&'s self, id: FieldId) -> Result<Unsizeable<'s>, FieldAccessError>;
194
195    /// Borrow mutably field inside this type
196    ///
197    /// Same as [`TypeInfo::field`], except that returned "reference" is mutable
198    fn field_mut<'s>(&'s mut self, id: FieldId) -> Result<UnsizeableMut<'s>, FieldAccessError>;
199}
200
201/// Static-type version of [`TypeInfoDynamic`]
202pub trait TypeInfo: TypeInfoDynamic + Sized {
203    #[allow(missing_docs)]
204    const INFO: &'static Type;
205}
206
207/// Immutable reference holder, returned by [`TypeInfoDynamic::field`] method
208///
209/// Can be downcasted to underlying type if underlying type is "nameable"
210pub struct Unsizeable<'a> {
211    ptr: *const (),
212    target_id: std::any::TypeId,
213    _lt: std::marker::PhantomData<&'a ()>,
214}
215
216impl<'a> Unsizeable<'a> {
217    #[doc(hidden)]
218    pub fn new(ptr: *const (), target_id: std::any::TypeId) -> Self {
219        Self {
220            ptr,
221            target_id,
222            _lt: std::marker::PhantomData,
223        }
224    }
225
226    /// Attempts to downcast field to immutable reference of particular type
227    ///
228    /// You need to be able to name this type in compile-time to succesfully downcast
229    ///
230    /// If `T` doesn't match actual type, [`Option::None`] will be returned
231    pub fn downcast_ref<T>(&self) -> Option<&'a T>
232    where
233        T: 'static,
234    {
235        if std::any::TypeId::of::<T>() != self.target_id {
236            return None;
237        }
238
239        unsafe {
240            let target_ptr = self.ptr as *const T;
241            target_ptr.as_ref()
242        }
243    }
244}
245
246/// Mutable reference holder, returned by [`TypeInfoDynamic::field_mut`] method
247///
248/// Can be downcasted to underlying type if underlying type is "nameable"
249
250pub struct UnsizeableMut<'a> {
251    ptr: *mut (),
252    target_id: std::any::TypeId,
253    _lt: std::marker::PhantomData<&'a ()>,
254}
255impl<'a> UnsizeableMut<'a> {
256    #[doc(hidden)]
257    pub fn new(ptr: *mut (), target_id: std::any::TypeId) -> Self {
258        Self {
259            ptr,
260            target_id,
261            _lt: std::marker::PhantomData,
262        }
263    }
264
265    /// Attempts to downcast field to mutable reference of particular type
266    ///
267    /// You need to be able to name this type in compile-time
268    /// If `T` doesn't match actual type, [`Option::None`] will be returned
269    pub fn downcast_mut<T>(&self) -> Option<&'a mut T>
270    where
271        T: 'static,
272    {
273        if std::any::TypeId::of::<T>() != self.target_id {
274            return None;
275        }
276
277        unsafe {
278            let target_ptr = self.ptr as *mut T;
279            target_ptr.as_mut()
280        }
281    }
282}
283
284use std::any::Any;
285
286use paste::paste;
287macro_rules! impl_primitive {
288    ($name:ty ) => {
289        paste! {
290            #[allow(unused)]
291            const  [<$name:upper _INFO>]: Type = Type {
292              ident: std::stringify!($name),
293              data: Data::Primitive,
294              // size: std::mem::size_of::<$name>(),
295              // alignment: std::mem::align_of::<$name>()
296            };
297
298            #[automatically_derived]
299            impl TypeInfoDynamic for $name {
300                fn get_dynamic(&self) ->  &'static Type {
301                    &[<$name:upper _INFO>]
302
303                }
304                fn construct_struct(&self, _args: Vec<Box<dyn Any>>) -> Result<Box<dyn Any>, RuntimeConstructError> {
305                     Err(RuntimeConstructError::Primitive)
306                }
307
308                fn construct_enum(
309                    &self,
310                    _variant: &'static str,
311                    _args: Vec<Box<dyn Any>>,
312                ) -> Result<Box<dyn Any>, RuntimeConstructError> {
313                         Err(RuntimeConstructError::Primitive)
314
315                }
316
317                fn field<'s>(&'s self, id: FieldId) -> Result<Unsizeable<'s>, FieldAccessError> {
318                    Err(FieldAccessError::Unit)
319                }
320                fn field_mut<'s>(&'s mut self, id: FieldId) -> Result<UnsizeableMut<'s>, FieldAccessError> {
321                    Err(FieldAccessError::Unit)
322
323                }
324
325            }
326            #[automatically_derived]
327            impl TypeInfo for $name {
328                const INFO: &'static Type = &[<$name:upper _INFO>];
329            }
330        }
331    };
332}
333
334impl_primitive!(u8);
335impl_primitive!(u16);
336impl_primitive!(u32);
337impl_primitive!(u64);
338impl_primitive!(u128);
339
340impl_primitive!(i8);
341impl_primitive!(i16);
342impl_primitive!(i32);
343impl_primitive!(i64);
344impl_primitive!(i128);
345
346impl_primitive!(usize);
347impl_primitive!(isize);
348
349impl_primitive!(String);
350
351impl_primitive!(f32);
352impl_primitive!(f64);
353
354mod __object_safety_check {
355    use super::TypeInfoDynamic;
356
357    fn __check_is_object_safe() -> Box<dyn TypeInfoDynamic> {
358        Box::new(100u32)
359    }
360}