1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
use std::any::{self, TypeId};
use std::fmt::{self, Debug};
use std::hash::{Hash, Hasher};

#[readonly::make]
#[derive(Clone, Copy)]
/// [std::any::TypeId] and [std::any::type_name] fused into one struct.
///
/// It acts as TypeId when hashed or compared, and it acts as type_name when printed.
/// Used to improve debuggability of type ids in hashmaps in particular.
/// ```
/// # use markdown_it::common::TypeKey;
/// struct A;
/// struct B;
///
/// let mut set = std::collections::HashSet::new();
///
/// set.insert(TypeKey::of::<A>());
/// set.insert(TypeKey::of::<B>());
///
/// assert!(set.contains(&TypeKey::of::<A>()));
/// dbg!(set);
/// ```
pub struct TypeKey {
    /// type id (read only)
    pub id:   TypeId,
    /// type name (read only)
    pub name: &'static str,
}

impl TypeKey {
    #[must_use]
    /// Similar to [TypeId::of](std::any::TypeId::of), returns `TypeKey`
    /// of the type this generic function has been instantiated with.
    pub fn of<T: ?Sized + 'static>() -> Self {
        Self { id: TypeId::of::<T>(), name: any::type_name::<T>() }
    }
}

impl Hash for TypeKey {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.id.hash(state);
    }
}

impl PartialEq for TypeKey {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id
    }
}

impl Eq for TypeKey {}

impl Debug for TypeKey {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.name)
    }
}

#[cfg(test)]
mod tests {
    use super::TypeKey;

    #[test]
    fn typekey_eq() {
        struct A;
        struct B;
        assert_eq!(TypeKey { id: std::any::TypeId::of::<A>(), name: "foo" },
                   TypeKey { id: std::any::TypeId::of::<A>(), name: "bar" });
        assert_ne!(TypeKey { id: std::any::TypeId::of::<A>(), name: "foo" },
                   TypeKey { id: std::any::TypeId::of::<B>(), name: "foo" });
    }

    #[test]
    fn typekey_of() {
        struct A;
        struct B;
        assert_eq!(TypeKey::of::<A>(), TypeKey::of::<A>());
        assert_ne!(TypeKey::of::<A>(), TypeKey::of::<B>());
    }
}