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}