my_ecs/ds/
types.rs

1use std::{
2    any::{self, TypeId},
3    borrow, cmp, fmt,
4    hash::Hash,
5    marker::PhantomData,
6    mem,
7    ops::Deref,
8    panic::UnwindSafe,
9    ptr,
10};
11
12/// Type information such as [`TypeId`], name, size, and alignment.
13///
14/// Also, the struct contains additional information shown below.
15/// - Type erased drop function.
16/// - Whether the type is [`Send`] or not.
17/// - Whether the type is [`Sync`] or not.
18/// - Whether the type is [`Clone`] or not and type erased clone function.
19/// - Whether the type is [`Default`] or not and type erased default function.
20///
21/// It's highly encouraged to use [`tinfo`] macro to construct this struct to
22/// avoid incorrect construction.
23///
24/// # Examples
25///
26/// ```
27/// use my_ecs::tinfo;
28///
29/// let x = tinfo!(i32);
30/// assert!(x.is_send && x.is_sync && x.is_default && x.is_clone);
31///
32/// let x = tinfo!(std::rc::Rc<i32>);
33/// assert!(!x.is_send && !x.is_sync && x.is_default && x.is_clone);
34/// ```
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub struct TypeInfo {
37    /// Type id.
38    pub ty: TypeId,
39
40    /// Type name.
41    ///
42    /// This field may differ from rust version to version.
43    pub name: &'static str,
44
45    /// Type size in bytes.
46    ///
47    /// Size must be a multiple of `align` including zero.
48    pub size: usize,
49
50    /// Type alignment in bytes.
51    ///
52    /// Alignment must be a power of two, at lease one.
53    pub align: usize,
54
55    /// Raw [`Drop::drop`] function pointer for the item type.
56    pub fn_drop: FnDropRaw,
57
58    /// Whether the type is [`Send`] or not.
59    pub is_send: bool,
60
61    /// Whether the type is [`Sync`] or not.
62    pub is_sync: bool,
63
64    /// Whether the type is [`Default`] or not.
65    pub is_default: bool,
66
67    /// Raw [`Default::default`] function pointer for the item type.
68    ///
69    /// If the item type is not [`Default`], calling this function causes panic.
70    pub fn_default: FnDefaultRaw,
71
72    /// Whether the type is [`Clone`] or not.
73    pub is_clone: bool,
74
75    /// Raw [`Clone::clone`] function pointer for the item type.
76    ///
77    /// If the item type is not [`Clone`], calling this function causes panic.
78    pub fn_clone: FnCloneRaw,
79}
80
81impl TypeInfo {
82    /// Creates a [`TypeInfo`] for the given type.
83    ///
84    /// # Examples
85    ///
86    /// ```
87    /// use my_ecs::ds::{TypeInfo, TypeHelper};
88    ///
89    /// #[derive(Clone)]
90    /// struct X;
91    ///
92    /// // Creates `TypeInfo` for the `X`.
93    /// let is_send = true;
94    /// let is_sync = true;
95    /// let fn_default = None;
96    /// let fn_clone = Some(TypeHelper::<X>::FN_CLONE);
97    /// let info = TypeInfo::new::<X>(is_send, is_sync, fn_default, fn_clone);
98    /// ```
99    pub fn new<T: 'static>(
100        is_send: bool,
101        is_sync: bool,
102        fn_default: Option<FnDefaultRaw>,
103        fn_clone: Option<FnCloneRaw>,
104    ) -> Self {
105        /// Calls [`drop_in_place`](ptr::drop_in_place) on the given pointer.
106        ///
107        /// # Safety
108        ///
109        /// `ptr` must be a properly aligned and nonnull pointer of a certain
110        /// type `T`.
111        /// `ptr` must be valid for writes.
112        /// `ptr` must be valid for dropping.
113        unsafe fn drop<T>(ptr: *mut u8) {
114            // Safety: Reflected in the function.
115            unsafe { (ptr as *mut T).drop_in_place() }
116        }
117
118        let (is_default, fn_default) = if let Some(fn_default) = fn_default {
119            (true, fn_default)
120        } else {
121            (false, unimpl_default as FnDefaultRaw)
122        };
123        let (is_clone, fn_clone) = if let Some(fn_clone) = fn_clone {
124            (true, fn_clone)
125        } else {
126            (false, unimpl_clone as FnCloneRaw)
127        };
128
129        TypeInfo {
130            ty: TypeId::of::<T>(),
131            name: any::type_name::<T>(),
132            size: size_of::<T>(),
133            align: align_of::<T>(),
134            fn_drop: drop::<T>,
135            is_send,
136            is_sync,
137            is_default,
138            fn_default,
139            is_clone,
140            fn_clone,
141        }
142    }
143
144    /// Returns true if the type information is about the given type.
145    ///
146    /// # Examples
147    ///
148    /// ```
149    /// use my_ecs::prelude::*;
150    ///
151    /// let tinfo = tinfo!(i32);
152    /// assert!(tinfo.is_type_of::<i32>());
153    /// ```
154    pub fn is_type_of<T: 'static>(&self) -> bool {
155        self.ty == TypeId::of::<T>()
156    }
157}
158
159/// Type-erased raw [`Drop::drop`] function pointer type.
160pub type FnDropRaw = unsafe fn(*mut u8);
161
162/// A helper struct used to determine whether a type implements traits like
163/// [`Send`], [`Sync`], and [`Clone`] by cooperating with helper traits like
164/// [`NotSend`] , [`NotSync`], and [`NotClone`].
165///
166/// Helper traits basically have associated constants meaning whether a type
167/// imeplements a trait such as `Send`. They are set to `false` by default, and
168/// all types implement the helper types by blanket implementation. In other
169/// words, all types are not `Send`, `Sync` by the helper traits, etc. But
170/// [`TypeHelper`] can overwrite it for types that actually implement those
171/// traits thanks to its trait bound. As a result, clients can be aware of
172/// whether a type impelments a certain trait through `TypeHelper`. This is
173/// especially useful when you make a library that receives anonymous type and
174/// you need such type information.
175///
176/// # How it works
177///
178/// If a struct has function `foo<T: Send>` and it also implement `Foo` which
179/// has the same signature function `foo<T>`, then rust will look for callable
180/// function in the order below.
181/// - Inherent function
182/// - Trait function
183///
184/// So if the type is `Send`, then rust chooses inherent function due to the
185/// search order.  But rust will choose trait function if the type is not `Send`
186/// due to the `T: Send` bound.
187///
188/// See <https://doc.rust-lang.org/reference/expressions/method-call-expr.html>
189/// (Document describes about methods, but I believe the same rule is applied
190/// to associated functions as well)
191///
192/// Here, more specific rules are written.
193/// 1. <https://rust-lang.github.io/rfcs/0195-associated-items.html#via-an-id_segment-prefix>
194/// 2. <https://rust-lang.github.io/rfcs/0195-associated-items.html#via-a-type_segment-prefix>
195///
196/// - `1` tells starting with ID_SEGMENT is equivalent to starting with
197///   TYPE_SEGMENT. 'A::b' is equivalent to '\<A\>::b'
198/// - `2` tells inherent members are prioritized over in-scope traits.
199pub struct TypeHelper<T: ?Sized>(PhantomData<T>);
200
201// === TypeHelper for `Send` ===
202
203/// A helper trait for [`TypeHelper`] to detect not [`Send`] types.
204///
205/// See [`TypeHelper`] documentation for more details.
206pub trait NotSend {
207    const IS_SEND: bool = false;
208}
209
210impl<T: ?Sized> NotSend for TypeHelper<T> {}
211
212impl<T: ?Sized + Send> TypeHelper<T> {
213    pub const IS_SEND: bool = true;
214}
215
216// === TypeHelper for `Sync` ===
217
218/// A helper trait for [`TypeHelper`] to detect not [`Sync`] types.
219///
220/// See [`TypeHelper`] documentation for more details.
221pub trait NotSync {
222    const IS_SYNC: bool = false;
223}
224
225impl<T: ?Sized> NotSync for TypeHelper<T> {}
226
227impl<T: ?Sized + Sync> TypeHelper<T> {
228    pub const IS_SYNC: bool = true;
229}
230
231// === TypeHelper for `UnwindSafe` ===
232
233/// A helper trait for [`TypeHelper`] to detect not [`UnwindSafe`] types.
234///
235/// See [`TypeHelper`] documentation for more details.
236pub trait NotUnwindSafe {
237    const IS_UNWIND_SAFE: bool = false;
238}
239
240impl<T: ?Sized> NotUnwindSafe for TypeHelper<T> {}
241
242impl<T: ?Sized + UnwindSafe> TypeHelper<T> {
243    pub const IS_UNWIND_SAFE: bool = true;
244}
245
246// === TypeHelper for `Debug` ===
247
248/// A helper trait for [`TypeHelper`] to detect not [`Debug`](fmt::Debug) types.
249///
250/// See [`TypeHelper`] documentation for more details.
251pub trait NotDebug {
252    const IS_DEBUG: bool = false;
253    const FN_FMT: FnFmtRaw = unimpl_fmt;
254}
255
256impl<T: ?Sized> NotDebug for TypeHelper<T> {}
257
258impl<T: fmt::Debug> TypeHelper<T> {
259    pub const IS_DEBUG: bool = true;
260    pub const FN_FMT: FnFmtRaw = Self::fn_fmt();
261
262    /// Returns a raw function pointer of [`Debug::fmt`](fmt::Debug::fmt) for
263    /// the given type.
264    ///
265    /// But the given type is not [`Debug`](fmt::Debug), then calling the
266    /// returned function will cause panic.
267    pub const fn fn_fmt() -> FnFmtRaw {
268        unsafe fn fmt<T: fmt::Debug>(
269            this: *const u8,
270            f: &mut fmt::Formatter<'_>,
271        ) -> Result<(), fmt::Error> {
272            let this = this.cast::<T>();
273            // Safety: Reflected in the `FnFmtRaw`.
274            unsafe { (*this).fmt(f) }
275        }
276
277        fmt::<T>
278    }
279}
280
281/// Type-erased raw [`Debug::fmt`](fmt::Debug::fmt) function pointer type.
282///
283/// To get a function pointer from a certain type, you can call
284/// [`TypeHelper::fn_fmt`]. Also, there is a helper type [`DebugHelper`]. It
285/// allows you to use the raw function pointer in `{:?}` patterns.
286///
287/// # Panics
288///
289/// If the function pointer was generated from a type that is not
290/// [`Debug`](fmt::Debug), calling the function causes panic.
291///
292/// # Safety
293///
294/// - `src` must be a properly aligned and non-null pointer of a certain type
295///   `T`.
296/// - `src` must be valid for read of `T`.
297pub type FnFmtRaw = unsafe fn(src: *const u8, &mut fmt::Formatter<'_>) -> Result<(), fmt::Error>;
298
299pub(crate) unsafe fn unimpl_fmt(
300    _: *const u8,
301    _: &mut fmt::Formatter<'_>,
302) -> Result<(), fmt::Error> {
303    unimplemented!("type is not `Debug`");
304}
305
306/// A helper type for the [`FnFmtRaw`].
307///
308/// This type implements [`Deubg`](fmt::Debug), therefore it's useful when you
309/// want to print something out using the `FnFmtRaw`.
310///
311/// # Examples
312///
313/// ```
314/// use my_ecs::ds::{TypeHelper, DebugHelper};
315///
316/// let value = 123_i32;
317/// let fn_fmt = TypeHelper::<i32>::fn_fmt();
318/// let helper = DebugHelper {
319///     f: fn_fmt,
320///     ptr: &value as *const i32 as *const u8
321/// };
322/// println!("{helper:?}");
323/// ```
324pub struct DebugHelper {
325    pub f: FnFmtRaw,
326    pub ptr: *const u8,
327}
328
329impl fmt::Debug for DebugHelper {
330    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331        unsafe { (self.f)(self.ptr, f) }
332    }
333}
334
335// === TypeHelper for `Default` ===
336
337/// A helper trait for [`TypeHelper`] to detect not [`Default`] types.
338///
339/// See [`TypeHelper`] documentation for more details.
340pub trait NotDefault {
341    const IS_DEFAULT: bool = false;
342    const FN_DEFAULT: FnDefaultRaw = unimpl_default;
343}
344
345impl<T: ?Sized> NotDefault for TypeHelper<T> {}
346
347impl<T: Default> TypeHelper<T> {
348    pub const IS_DEFAULT: bool = true;
349    pub const FN_DEFAULT: FnDefaultRaw = Self::fn_default();
350
351    /// Returns raw function pointer of [`Default::default`] for the given type.
352    ///
353    /// But the given type is not [`Default`], then calling the returned
354    /// function will cause panic.
355    pub const fn fn_default() -> FnDefaultRaw {
356        unsafe fn default<T: Default>(dst: *mut u8) {
357            let src = <T as Default>::default();
358            let dst = dst.cast::<T>();
359
360            // Safety: Reflected in the `FnDefaultRaw`.
361            unsafe { ptr::copy_nonoverlapping(&src as *const T, dst, 1) };
362            mem::forget(src);
363        }
364
365        default::<T>
366    }
367}
368
369/// Type-erased raw [`Default::default`] function pointer type.
370///
371/// Call [`TypeHelper::fn_default`] to get the function pointer.
372///
373/// # Panics
374///
375/// If the function pointer was generated from a type that is not [`Default`],
376/// calling the function causes panic.
377///
378/// # Safety
379///
380/// - `dst` must be a properly aligned and nonnull pointer of a certain type
381///   `T`.
382/// - `dst` must be valid for write of `T`.
383pub type FnDefaultRaw = unsafe fn(dst: *mut u8);
384
385pub(crate) unsafe fn unimpl_default(_: *mut u8) {
386    unimplemented!("type is not `Default`");
387}
388
389// === TypeHelper for `Clone` ===
390
391/// A helper trait for [`TypeHelper`] to detect not [`Clone`] types.
392///
393/// See [`TypeHelper`] documentation for more details.
394pub trait NotClone {
395    const IS_CLONE: bool = false;
396    const FN_CLONE: FnCloneRaw = unimpl_clone;
397}
398
399impl<T: ?Sized> NotClone for TypeHelper<T> {}
400
401impl<T: Clone> TypeHelper<T> {
402    pub const IS_CLONE: bool = true;
403    pub const FN_CLONE: FnCloneRaw = Self::fn_clone();
404
405    /// Returns raw function pointer of [`Clone::clone`] for the given type.
406    ///
407    /// But the given type is not [`Clone`], then calling the returned function
408    /// will cause panic.
409    pub const fn fn_clone() -> FnCloneRaw {
410        unsafe fn clone<T: Clone>(src: *const u8, dst: *mut u8) {
411            let src = src.cast::<T>();
412            let dst = dst.cast::<T>();
413
414            // Safety: Reflected in the `FnCloneRaw`.
415            unsafe {
416                let src_cloned = (*src).clone();
417                ptr::copy_nonoverlapping(&src_cloned as *const T, dst, 1);
418                mem::forget(src_cloned);
419            }
420        }
421
422        clone::<T>
423    }
424}
425
426/// Type-erased raw [`Clone::clone`] function pointer type.
427///
428/// Call [`TypeHelper::fn_clone`] to get the function pointer.
429///
430/// # Panics
431///
432/// If the function pointer was generated from a type that is not [`Clone`],
433/// calling the function causes panic.
434///
435/// # Safety
436///
437/// This function calls [`copy_nonoverlapping`](ptr::copy_nonoverlapping)
438/// internally. Therefore, function follows the same safety conditions like
439/// below.
440///
441/// - Both `src` and `dst` must be properly aligned and nonnull pointers of a
442///   certain type `T`.
443/// - `src` must be valid for read of `T`.
444/// - `dst` must be valid for write of `T`.
445/// - Region of `src` memory must not overlap with region of `dst` memory.
446pub type FnCloneRaw = unsafe fn(src: *const u8, dst: *mut u8);
447
448pub(crate) unsafe fn unimpl_clone(_: *const u8, _: *mut u8) {
449    unimplemented!("type is not `Clone`");
450}
451
452// === TypeHelper for EqualType ===
453
454/// A helper trait for [`TypeHelper`] to detect not equal types.
455///
456/// See [`TypeHelper`] documentation for more details.
457pub trait NotEqualType {
458    const IS_EQUAL_TYPE: bool = false;
459}
460
461impl<T> NotEqualType for TypeHelper<T> {}
462
463impl<T> TypeHelper<(T, T)> {
464    pub const IS_EQUAL_TYPE: bool = true;
465}
466
467/// Creates [`TypeInfo`] from the given type and reflects whether the type
468/// implements [`Send`], [`Sync`], [`Default`], and [`Clone`] to the TypeInfo.
469///
470/// If you want your own type name, you can call this macro like
471/// `tinfo!(T, "new-name")`.
472///
473/// This macro exploits Rust's function look-up procedures to determine if the
474/// type implements the traits. See [`TypeHelper`] for more details.
475///
476/// # Examples
477///
478/// ```
479/// use my_ecs::prelude::*;
480///
481/// // - Clone detection
482///
483/// struct A;
484/// struct B;
485/// #[derive(Clone)]
486/// struct C;
487/// #[derive(Clone)]
488/// struct D;
489///
490/// let a = tinfo!(A); // for non-cloneable type A.
491/// let b = tinfo!(B); // for non-cloneable type B.
492/// let c = tinfo!(C); // for cloneable type C.
493/// let d = tinfo!(D); // for cloneable type D.
494///
495/// assert_eq!(a.fn_clone, b.fn_clone); // A and B have the same dummy clone function.
496/// assert_ne!(a.fn_clone, c.fn_clone); // But C has its own clone function.
497/// assert_ne!(a.fn_clone, d.fn_clone); // And so does D.
498/// assert_ne!(c.fn_clone, d.fn_clone);
499///
500/// // - Send & Sync detection
501///
502/// struct SendSync(u8); // Both Send and Sync.
503/// struct NotSendSync(*mut u8); // Neither Send nor Sync.
504///
505/// let send_sync = tinfo!(SendSync);
506/// let not_send_sync = tinfo!(NotSendSync);
507///
508/// assert!(send_sync.is_send);
509/// assert!(send_sync.is_sync);
510/// assert!(!not_send_sync.is_send);
511/// assert!(!not_send_sync.is_sync);
512///
513/// // Incorrect usage
514///
515/// fn is_clone<T: 'static>() -> bool {
516///     // The macro doesn't work if the type is passed through generic
517///     // parameter.
518///     tinfo!(T).is_clone
519/// }
520///
521/// // assert!(is_clone::<C>());
522/// # assert!(!is_clone::<C>());
523///
524/// ```
525#[macro_export]
526macro_rules! tinfo {
527    ($ty:ty) => {{
528        #[allow(unused_imports)]
529        use $crate::ds::{NotClone, NotDefault, NotSend, NotSync, TypeHelper, TypeInfo};
530
531        TypeInfo::new::<$ty>(
532            TypeHelper::<$ty>::IS_SEND,
533            TypeHelper::<$ty>::IS_SYNC,
534            TypeHelper::<$ty>::IS_DEFAULT.then_some(TypeHelper::<$ty>::FN_DEFAULT),
535            TypeHelper::<$ty>::IS_CLONE.then_some(TypeHelper::<$ty>::FN_CLONE),
536        )
537    }};
538    ($ty:ty, $name:literal) => {{
539        #[allow(unused_imports)]
540        use $crate::ds::types::{NotClone, NotDefault, NotSend, NotSync, TypeHelper, TypeInfo};
541
542        let mut tinfo = TypeInfo::new::<$ty>(
543            TypeHelper::<$ty>::IS_SEND,
544            TypeHelper::<$ty>::IS_SYNC,
545            TypeHelper::<$ty>::IS_DEFAULT.then_some(TypeHelper::<$ty>::FN_DEFAULT),
546            TypeHelper::<$ty>::IS_CLONE.then_some(TypeHelper::<$ty>::FN_CLONE),
547        );
548        tinfo.name = $name;
549        tinfo
550    }};
551}
552
553// The macro is exported and will be shown up in crate level. So we can use the
554// macro like 'crate::tinfo!(..)'. In doc comments, however, the link to the
555// macro is generated something like 'crate::ds::types::tinfo'. Re-exporting
556// like this helps us address the issue.
557pub use crate::tinfo;
558
559/// Represents an extended [`TypeId`] with type name.
560///
561/// This would be useful for enriching debug messages, but the type name is only
562/// included when `check` feature is enabled.
563#[cfg_attr(not(feature = "check"), repr(transparent))]
564pub struct TypeIdExt {
565    inner: TypeId,
566    #[cfg(feature = "check")]
567    name: &'static str,
568}
569
570#[cfg(feature = "check")]
571impl fmt::Debug for TypeIdExt {
572    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
573        #[cfg(not(feature = "check"))]
574        {
575            self.inner.fmt(f)
576        }
577
578        #[cfg(feature = "check")]
579        {
580            write!(f, "TypeIdExt({})", self.name)
581        }
582    }
583}
584
585impl TypeIdExt {
586    /// Creates a new [`TypeIdExt`] from the given [`TypeId`].
587    ///
588    /// If `check` feature is enabled, type name is set to blank string. To set
589    /// a proper name, call [`TypeIdExt::with`].
590    pub const fn new(ty: TypeId) -> Self {
591        Self {
592            inner: ty,
593            #[cfg(feature = "check")]
594            name: "",
595        }
596    }
597
598    /// Creates a new [`TypeIdExt`] with the given name.
599    ///
600    /// If `check` feature is disabled, this is no-op.
601    #[cfg_attr(not(feature = "check"), allow(unused_variables))]
602    pub const fn with(self, name: &'static str) -> Self {
603        #[cfg(feature = "check")]
604        {
605            let mut this = self;
606            this.name = name;
607        }
608        self
609    }
610
611    /// Creates a new [`TypeIdExt`] from the given type.
612    pub fn of<T: ?Sized + 'static>() -> Self {
613        Self {
614            inner: TypeId::of::<T>(),
615            #[cfg(feature = "check")]
616            name: any::type_name::<T>(),
617        }
618    }
619
620    /// Returns type name.
621    #[cfg(feature = "check")]
622    pub const fn name(&self) -> &'static str {
623        self.name
624    }
625}
626
627impl Deref for TypeIdExt {
628    type Target = TypeId;
629
630    fn deref(&self) -> &Self::Target {
631        &self.inner
632    }
633}
634
635impl Clone for TypeIdExt {
636    fn clone(&self) -> Self {
637        *self
638    }
639}
640
641impl Copy for TypeIdExt {}
642
643impl PartialEq<Self> for TypeIdExt {
644    fn eq(&self, other: &Self) -> bool {
645        self.inner == other.inner
646    }
647}
648
649impl PartialEq<TypeId> for TypeIdExt {
650    fn eq(&self, other: &TypeId) -> bool {
651        &self.inner == other
652    }
653}
654
655impl Eq for TypeIdExt {}
656
657impl PartialOrd<Self> for TypeIdExt {
658    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
659        Some(self.cmp(other))
660    }
661}
662
663impl PartialOrd<TypeId> for TypeIdExt {
664    fn partial_cmp(&self, other: &TypeId) -> Option<cmp::Ordering> {
665        self.inner.partial_cmp(other)
666    }
667}
668
669impl Ord for TypeIdExt {
670    fn cmp(&self, other: &Self) -> cmp::Ordering {
671        self.inner.cmp(&other.inner)
672    }
673}
674
675impl Hash for TypeIdExt {
676    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
677        self.inner.hash(state)
678    }
679}
680
681impl borrow::Borrow<TypeId> for TypeIdExt {
682    fn borrow(&self) -> &TypeId {
683        &self.inner
684    }
685}
686
687impl From<&TypeInfo> for TypeIdExt {
688    fn from(value: &TypeInfo) -> Self {
689        Self::new(value.ty).with(value.name)
690    }
691}
692
693/// A [`TypeIdExt`] with a salt type.
694///
695/// By adding a salt type, this type can be different from each other. For
696/// instance, if we have `ATypeId<i32>` and `ATypeId<u32>`, they are completely
697/// different types from perspective of Rust.
698#[repr(transparent)]
699pub struct ATypeId<Salt> {
700    inner: TypeIdExt,
701    _marker: PhantomData<Salt>,
702}
703
704impl<Salt> fmt::Debug for ATypeId<Salt> {
705    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
706        self.inner.fmt(f)
707    }
708}
709
710impl<Salt> ATypeId<Salt> {
711    /// Creates a new [`ATypeId`] from the given [`TypeIdExt`].
712    pub const fn new(ty: TypeIdExt) -> Self {
713        Self {
714            inner: ty,
715            _marker: PhantomData,
716        }
717    }
718
719    /// Creates a new [`ATypeId`] from the given type.
720    pub fn of<T: ?Sized + 'static>() -> Self {
721        Self {
722            inner: TypeIdExt::of::<T>(),
723            _marker: PhantomData,
724        }
725    }
726
727    /// Converts [`ATypeId`] into [`TypeIdExt`] by unwraping self.
728    pub fn into_inner(self) -> TypeIdExt {
729        self.inner
730    }
731
732    /// Returns a shared reference to inner [`TypeIdExt`].
733    pub fn get_inner(&self) -> &TypeIdExt {
734        &self.inner
735    }
736}
737
738impl<Salt> Deref for ATypeId<Salt> {
739    type Target = TypeIdExt;
740
741    fn deref(&self) -> &Self::Target {
742        &self.inner
743    }
744}
745
746impl<Salt> Clone for ATypeId<Salt> {
747    fn clone(&self) -> Self {
748        *self
749    }
750}
751
752impl<Salt> Copy for ATypeId<Salt> {}
753
754impl<Salt> PartialEq for ATypeId<Salt> {
755    fn eq(&self, other: &Self) -> bool {
756        self.inner.eq(&other.inner)
757    }
758}
759
760impl<Salt> Eq for ATypeId<Salt> {}
761
762impl<Salt> PartialOrd for ATypeId<Salt> {
763    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
764        Some(self.cmp(other))
765    }
766}
767
768impl<Salt> Ord for ATypeId<Salt> {
769    fn cmp(&self, other: &Self) -> cmp::Ordering {
770        self.inner.cmp(&other.inner)
771    }
772}
773
774impl<Salt> Hash for ATypeId<Salt> {
775    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
776        self.inner.hash(state)
777    }
778}
779
780impl<Salt> From<&TypeInfo> for ATypeId<Salt> {
781    fn from(value: &TypeInfo) -> Self {
782        Self {
783            inner: TypeIdExt::from(value),
784            _marker: PhantomData,
785        }
786    }
787}
788
789#[cfg(test)]
790mod tests {
791    use super::*;
792
793    #[test]
794    #[allow(unused)]
795    #[rustfmt::skip]
796    fn test_type_helper() {
797        use std::alloc::{self, Layout};
798
799        // Detects `Debug`.
800        #[derive(Debug)]
801        struct DebugTrue(i32);
802        struct DebugFalse(i32);
803        const _: () = {
804            assert!(TypeHelper::<DebugTrue>::IS_DEBUG);
805            assert!(!TypeHelper::<DebugFalse>::IS_DEBUG);
806        };
807
808        // Validates `TypeHelper::fn_fmt`.
809        let v = DebugTrue(42);
810        let helper = DebugHelper {
811            f: TypeHelper::<DebugTrue>::fn_fmt(),
812            ptr: &v as *const _ as *const u8
813        };
814        let formatted = format!("{helper:?}");
815        assert_eq!(&formatted, "DebugTrue(42)");
816
817        // Detects `Default`.
818        struct DefaultInner(i32);
819        impl Default for DefaultInner { fn default() -> Self { Self(42) } }
820        #[derive(Default)]
821        struct DefaultTrue(DefaultInner);
822        struct DefaultFalse;
823        const _: () = {
824            assert!(TypeHelper::<DefaultTrue>::IS_DEFAULT);
825            assert!(!TypeHelper::<DefaultFalse>::IS_DEFAULT);
826        };
827
828        // Validates `TypeHelper::fn_default`.
829        let layout = Layout::new::<DefaultTrue>();
830        let fn_default = TypeHelper::<DefaultTrue>::fn_default();
831        unsafe {
832            let buf = alloc::alloc(layout);
833            fn_default(buf);
834            let v = &*buf.cast::<DefaultTrue>();
835            assert_eq!(v.0.0, 42);
836            alloc::dealloc(buf, layout);
837        }
838
839        // Detects `Clone`.
840        #[derive(Clone)]
841        struct CloneTrue(i32);
842        struct CloneFalse(i32);
843        const _: () = {
844            assert!(TypeHelper::<CloneTrue>::IS_CLONE);
845            assert!(!TypeHelper::<CloneFalse>::IS_CLONE);
846        };
847
848        // Validates `TypeHelper::fn_clone`.
849        let (src, mut dst) = (CloneTrue(42), CloneTrue(0));
850        let fn_clone = TypeHelper::<CloneTrue>::fn_clone();
851        unsafe {
852            let src_ptr = &src as *const _ as *const u8;
853            let dst_ptr = &mut dst as *mut _ as *mut u8;
854            fn_clone(src_ptr, dst_ptr);
855        }
856        assert_eq!(src.0, dst.0);
857
858        // Detects `Send`.
859        struct SendTrue;
860        struct SendFalse(*const ());
861        const _: () = {
862            assert!(TypeHelper::<SendTrue>::IS_SEND);
863            assert!(!TypeHelper::<SendFalse>::IS_SEND);
864        };
865
866        // Detects `Sync`.
867        struct SyncTrue;
868        struct SyncFalse(*const ());
869        const _: () = {
870            assert!(TypeHelper::<SyncTrue>::IS_SYNC);
871            assert!(!TypeHelper::<SyncFalse>::IS_SYNC);
872        };
873
874        // Detects `EqualType`.
875        struct A;
876        struct B;
877        const _: () = {
878            assert!(TypeHelper::<(A, A)>::IS_EQUAL_TYPE);
879            assert!(!TypeHelper::<(A, B)>::IS_EQUAL_TYPE);
880        };
881    }
882}