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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::path::Path;

use super::hash;

#[derive(Serialize, Deserialize, Debug, Eq)]
pub struct HashValue<T>(u64, PhantomData<T>)
where
    T: Hash + ?Sized;

impl<T> HashValue<T>
where
    T: Hash + ?Sized,
{
    pub fn zero() -> Self {
        HashValue(0, PhantomData)
    }
}

impl<T> Clone for HashValue<T>
where
    T: Hash + ?Sized,
{
    fn clone(&self) -> Self {
        HashValue(self.0, self.1)
    }
}

impl<T> Copy for HashValue<T> where T: Hash + ?Sized {}

impl<T> PartialEq for HashValue<T>
where
    T: Hash + ?Sized,
{
    fn eq(&self, other: &Self) -> bool {
        self.0.eq(&other.0)
    }
}

impl<T> Hash for HashValue<T>
where
    T: Hash + ?Sized,
{
    fn hash<H>(&self, state: &mut H)
    where
        H: Hasher,
    {
        self.0.hash(state);
    }
}

impl<F> From<F> for HashValue<str>
where
    F: AsRef<str>,
{
    fn from(v: F) -> Self {
        HashValue(hash::hash64(v.as_ref()), PhantomData)
    }
}

impl<T> From<T> for HashValue<Path>
where
    T: AsRef<Path>,
{
    fn from(v: T) -> Self {
        HashValue(hash::hash64(v.as_ref()), PhantomData)
    }
}

impl<T> PartialEq<T> for HashValue<str>
where
    T: AsRef<str>,
{
    fn eq(&self, other: &T) -> bool {
        self.0.eq(&hash::hash64(other.as_ref()))
    }
}

impl<T> PartialEq<T> for HashValue<Path>
where
    T: AsRef<Path>,
{
    fn eq(&self, other: &T) -> bool {
        self.0.eq(&hash::hash64(other.as_ref()))
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::utils::hash::FastHashSet;

    #[test]
    fn hash_str() {
        let hash = HashValue::<str>::from("hash_str");
        assert_eq!(hash, "hash_str");
        assert!(hash != "other_str");
    }

    #[test]
    fn collections() {
        let mut set = FastHashSet::<HashValue<str>>::default();
        set.insert(HashValue::from("asdasd"));
        set.insert(HashValue::from("asdasd"));
        set.insert(HashValue::from("asdasd"));
        set.insert(HashValue::from("asdasd"));
        assert_eq!(set.len(), 1);
        assert_eq!(
            set.get(&("asdasd".into())),
            Some(&HashValue::from("asdasd"))
        );
    }

    #[test]
    fn hash_path() {
        let _ = HashValue::<Path>::from("str_path");
    }
}