cache_any/
cacheable.rs

1use std::fmt::Debug;
2use std::io::Cursor;
3use std::sync::Arc;
4use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
5
6/// used to convert [`Cacheable`] to bytes and vice versa.
7pub trait Cacheable: Debug {
8    /// Convert [`Cacheable`] to bytes.
9    fn to_bytes(&self) -> Vec<u8>;
10
11    /// Convert bytes to [`Cacheable`].
12    fn from_bytes(bytes: &[u8]) -> anyhow::Result<Self>
13    where
14        Self: Sized;
15
16    fn to_hex(&self) -> String {
17        hex::encode(self.to_bytes())
18    }
19
20    fn from_hex(hex: &str) -> anyhow::Result<Self>
21    where
22        Self: Sized,
23    {
24        let bytes = hex::decode(hex)?;
25        Self::from_bytes(&bytes)
26    }
27}
28
29impl Cacheable for () {
30    fn to_bytes(&self) -> Vec<u8> {
31        vec![]
32    }
33
34    fn from_bytes(_bytes: &[u8]) -> anyhow::Result<Self> {
35        Ok(())
36    }
37}
38
39impl<T: Cacheable> Cacheable for Arc<T> {
40    fn to_bytes(&self) -> Vec<u8> {
41        self.as_ref().to_bytes()
42    }
43
44    fn from_bytes(bytes: &[u8]) -> anyhow::Result<Self>
45    where
46        Self: Sized,
47    {
48        Ok(Arc::new(T::from_bytes(bytes)?))
49    }
50}
51
52impl Cacheable for Vec<u8> {
53    fn to_bytes(&self) -> Vec<u8> {
54        self.clone()
55    }
56
57    fn from_bytes(bytes: &[u8]) -> anyhow::Result<Self>
58    where
59        Self: Sized,
60    {
61        Ok(bytes.to_vec())
62    }
63}
64
65impl Cacheable for String {
66    fn to_bytes(&self) -> Vec<u8> {
67        self.as_bytes().to_vec()
68    }
69
70    fn from_bytes(bytes: &[u8]) -> anyhow::Result<Self>
71    where
72        Self: Sized,
73    {
74        Ok(Self::from_utf8(bytes.to_vec())?)
75    }
76}
77
78macro_rules! impl_numeric {
79    ($ty: ty) => {
80        impl Cacheable for $ty {
81            fn to_bytes(&self) -> Vec<u8> {
82                let num = *self as u128;
83                let mut wtr = Vec::with_capacity(16);
84                wtr.write_u128::<BigEndian>(num).unwrap();
85
86                wtr
87            }
88
89            fn from_bytes(bytes: &[u8]) -> anyhow::Result<Self>
90            where
91                Self: Sized
92            {
93                let mut rdr = Cursor::new(bytes);
94                let num = rdr.read_u128::<BigEndian>().unwrap();
95
96                Ok(num as $ty)
97            }
98        }
99    };
100    ($($ty: ty),+ $(,)?) => {
101        $(
102            impl_numeric!($ty);
103        )*
104    };
105}
106
107impl_numeric!(
108    u128, i128,
109    u64, i64,
110    u32, i32,
111    u16, i16,
112    u8, i8,
113    usize, isize,
114);
115
116impl Cacheable for bool {
117    fn to_bytes(&self) -> Vec<u8> {
118        if *self {
119            Cacheable::to_bytes(&1)
120        } else {
121            Cacheable::to_bytes(&0)
122        }
123    }
124
125    fn from_bytes(bytes: &[u8]) -> anyhow::Result<Self>
126    where
127        Self: Sized,
128    {
129        Ok(!bytes.iter().all(|byte| *byte == 0))
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136    use rand::{random, Rng, thread_rng};
137    use rand::distributions::Alphanumeric;
138
139    #[tokio::test]
140    async fn it_works() -> anyhow::Result<()> { Ok(()) }
141
142    #[test]
143    fn test_string() -> anyhow::Result<()> {
144        for _ in 0..1024 {
145            let t: String = (0..32)
146                .map(|_| thread_rng().sample(Alphanumeric) as char)
147                .collect();
148
149            let v = Cacheable::to_bytes(&t);
150            let d: String = Cacheable::from_bytes(&v).unwrap();
151
152            assert_eq!(t, d);
153        }
154
155        Ok(())
156    }
157
158    #[test]
159    fn test_numeric() -> anyhow::Result<()> {
160        macro_rules! test {
161            ($ty: ty) => {
162                for _ in 0..1024 {
163                    let num: $ty = random();
164                    let v = Cacheable::to_bytes(&num);
165                    let d: $ty = Cacheable::from_bytes(&v).unwrap();
166                    assert_eq!(num, d);
167                }
168            };
169            ($($ty: ty),+ $(,)?) => {
170                $(test!($ty);)+
171            };
172        }
173
174        test!(
175            u128, i128,
176            u64, i64,
177            u32, i32,
178            u16, i16,
179            u8, i8,
180            usize, isize,
181        );
182
183        macro_rules! test_arc {
184            ($ty: ty) => {
185                for _ in 0..1024 {
186                    let num: $ty = rand::thread_rng().gen();
187                    let num = Arc::new(num);
188                    let v = Cacheable::to_bytes(&num);
189                    let arc_d: Arc<$ty> = Cacheable::from_bytes(&v).unwrap();
190                    let d: $ty = Cacheable::from_bytes(&v).unwrap();
191                    assert_eq!(num, arc_d);
192                    assert_eq!(d, *arc_d);
193                }
194            };
195            ($($ty: ty),+ $(,)?) => {
196                $(test_arc!($ty);)+
197            };
198        }
199
200        test_arc!(
201            u128, i128,
202            u64, i64,
203            u32, i32,
204            u16, i16,
205            u8, i8,
206            usize, isize,
207        );
208
209        Ok(())
210    }
211
212    #[test]
213    fn test_boolean() -> anyhow::Result<()> {
214        for _ in 0..1024 {
215            let b: bool = random();
216            let v = b.to_bytes();
217            let d: bool = Cacheable::from_bytes(&v).unwrap();
218
219            assert_eq!(b, d);
220        }
221
222        Ok(())
223    }
224}