const_struct_version/
lib.rs

1#![doc = include_str!("../README.md")]
2#![deny(clippy::all, clippy::pedantic)]
3#![allow(clippy::uninlined_format_args, clippy::needless_doctest_main)]
4
5use std::any::type_name;
6
7use sha1::Digest;
8
9#[doc(hidden)]
10pub mod __private {
11    pub use sha1;
12
13    pub fn execute_if_serde_enabled(hasher: &mut sha1::Sha1, f: impl FnOnce(&mut sha1::Sha1)) {
14        if cfg!(feature = "serde-attributes") {
15            f(hasher);
16        }
17    }
18}
19
20pub trait StructVersion {
21    fn version() -> String;
22}
23
24#[cfg(feature = "derive")]
25pub use const_struct_version_derive::StructVersion;
26
27macro_rules! impl_struct_version {
28    ($($t:ty),*) => {
29        $(
30            impl StructVersion for $t {
31                fn version() -> String {
32                    let mut hasher = sha1::Sha1::new();
33                    hasher.update(type_name::<$t>());
34                    format!("{:x}", hasher.finalize())
35                }
36            }
37        )*
38    };
39}
40
41impl_struct_version!(
42    (),
43    bool,
44    char,
45    u8,
46    i8,
47    u16,
48    i16,
49    u32,
50    i32,
51    u64,
52    i64,
53    u128,
54    i128,
55    f32,
56    f64,
57    String,
58    std::time::Duration,
59    std::time::Instant
60);
61
62#[cfg_attr(test, mutants::skip)]
63impl<T: StructVersion> StructVersion for Vec<T> {
64    fn version() -> String {
65        let mut hasher = sha1::Sha1::new();
66        hasher.update("Vec");
67        hasher.update(type_name::<T>());
68        format!("{:x}", hasher.finalize())
69    }
70}
71impl<T: StructVersion> StructVersion for Option<T> {
72    fn version() -> String {
73        let mut hasher = sha1::Sha1::new();
74        hasher.update("Option");
75        hasher.update(type_name::<T>());
76        format!("{:x}", hasher.finalize())
77    }
78}
79impl<T: StructVersion> StructVersion for Box<T> {
80    fn version() -> String {
81        let mut hasher = sha1::Sha1::new();
82        hasher.update("Box");
83        hasher.update(type_name::<T>());
84        format!("{:x}", hasher.finalize())
85    }
86}
87impl<T: StructVersion> StructVersion for std::rc::Rc<T> {
88    fn version() -> String {
89        let mut hasher = sha1::Sha1::new();
90        hasher.update("Rc");
91        hasher.update(type_name::<T>());
92        format!("{:x}", hasher.finalize())
93    }
94}
95impl<T: StructVersion> StructVersion for std::sync::Arc<T> {
96    fn version() -> String {
97        let mut hasher = sha1::Sha1::new();
98        hasher.update("Arc");
99        hasher.update(type_name::<T>());
100        format!("{:x}", hasher.finalize())
101    }
102}
103
104impl<K, V, S> StructVersion for std::collections::HashMap<K, V, S>
105where
106    K: StructVersion,
107    V: StructVersion,
108    S: std::hash::BuildHasher,
109{
110    fn version() -> String {
111        let mut hasher = sha1::Sha1::new();
112        hasher.update("HashMap");
113        hasher.update(type_name::<K>());
114        hasher.update(type_name::<V>());
115        format!("{:x}", hasher.finalize())
116    }
117}
118
119impl<K, V> StructVersion for std::collections::BTreeMap<K, V>
120where
121    K: StructVersion,
122    V: StructVersion,
123{
124    fn version() -> String {
125        let mut hasher = sha1::Sha1::new();
126        hasher.update("BTreeMap");
127        hasher.update(type_name::<K>());
128        hasher.update(type_name::<V>());
129        format!("{:x}", hasher.finalize())
130    }
131}
132
133impl<K, S> StructVersion for std::collections::HashSet<K, S>
134where
135    K: StructVersion,
136    S: std::hash::BuildHasher,
137{
138    fn version() -> String {
139        let mut hasher = sha1::Sha1::new();
140        hasher.update("HashSet");
141        hasher.update(type_name::<K>());
142        format!("{:x}", hasher.finalize())
143    }
144}
145
146impl<K> StructVersion for std::collections::BTreeSet<K>
147where
148    K: StructVersion,
149{
150    fn version() -> String {
151        let mut hasher = sha1::Sha1::new();
152        hasher.update("BTreeSet");
153        hasher.update(type_name::<K>());
154        format!("{:x}", hasher.finalize())
155    }
156}
157
158impl<T> StructVersion for std::collections::LinkedList<T>
159where
160    T: StructVersion,
161{
162    fn version() -> String {
163        let mut hasher = sha1::Sha1::new();
164        hasher.update("LinkedList");
165        hasher.update(type_name::<T>());
166        format!("{:x}", hasher.finalize())
167    }
168}
169
170impl<T> StructVersion for std::collections::VecDeque<T>
171where
172    T: StructVersion,
173{
174    fn version() -> String {
175        let mut hasher = sha1::Sha1::new();
176        hasher.update("VecDeque");
177        hasher.update(type_name::<T>());
178        format!("{:x}", hasher.finalize())
179    }
180}
181
182impl<T> StructVersion for std::collections::BinaryHeap<T>
183where
184    T: StructVersion,
185{
186    fn version() -> String {
187        let mut hasher = sha1::Sha1::new();
188        hasher.update("BinaryHeap");
189        hasher.update(type_name::<T>());
190        format!("{:x}", hasher.finalize())
191    }
192}
193
194#[cfg(feature = "chrono")]
195impl<Tz: chrono::TimeZone> StructVersion for chrono::DateTime<Tz> {
196    fn version() -> String {
197        let mut hasher = sha1::Sha1::new();
198        hasher.update("DateTime");
199        hasher.update(type_name::<Tz>());
200        format!("{:x}", hasher.finalize())
201    }
202}
203
204#[cfg(feature = "url")]
205impl StructVersion for url::Url {
206    fn version() -> String {
207        let mut hasher = sha1::Sha1::new();
208        hasher.update("Url");
209        format!("{:x}", hasher.finalize())
210    }
211}
212
213#[cfg(feature = "uuid")]
214impl StructVersion for uuid::Uuid {
215    fn version() -> String {
216        let mut hasher = sha1::Sha1::new();
217        hasher.update("Uuid");
218        format!("{:x}", hasher.finalize())
219    }
220}
221
222#[cfg(feature = "indexmap")]
223impl<K, V, S> StructVersion for indexmap::IndexMap<K, V, S>
224where
225    K: StructVersion,
226    V: StructVersion,
227    S: std::hash::BuildHasher,
228{
229    fn version() -> String {
230        let mut hasher = sha1::Sha1::new();
231        hasher.update("IndexMap");
232        hasher.update(type_name::<K>());
233        hasher.update(type_name::<V>());
234        format!("{:x}", hasher.finalize())
235    }
236}
237
238#[cfg(feature = "indexmap")]
239impl<K> StructVersion for indexmap::IndexSet<K>
240where
241    K: StructVersion,
242{
243    fn version() -> String {
244        let mut hasher = sha1::Sha1::new();
245        hasher.update("IndexSet");
246        hasher.update(type_name::<K>());
247        format!("{:x}", hasher.finalize())
248    }
249}
250
251#[cfg(test)]
252mod tests {
253    macro_rules! test {
254        ($name:ident, $t:ty) => {
255            #[test]
256            fn $name() {
257                let version = <$t as super::StructVersion>::version();
258                insta::assert_snapshot!(version);
259            }
260        };
261    }
262
263    test!(test_bool, bool);
264    test!(test_char, char);
265    test!(test_u8, u8);
266    test!(test_i8, i8);
267    test!(test_u16, u16);
268    test!(test_i16, i16);
269    test!(test_u32, u32);
270    test!(test_i32, i32);
271    test!(test_u64, u64);
272    test!(test_i64, i64);
273    test!(test_u128, u128);
274    test!(test_i128, i128);
275    test!(test_f32, f32);
276    test!(test_f64, f64);
277    test!(test_string, String);
278    test!(test_duration, std::time::Duration);
279    test!(test_instant, std::time::Instant);
280    test!(test_vec_u32, Vec<u32>);
281    test!(test_option_u32, Option<u32>);
282    test!(test_box_u32, Box<u32>);
283    test!(test_rc_u32, std::rc::Rc<u32>);
284    test!(test_arc_u32, std::sync::Arc<u32>);
285    test!(test_hash_map_u32_u32, std::collections::HashMap<u32, u32>);
286    test!(test_b_tree_map_u32_u32, std::collections::BTreeMap<u32, u32>);
287    test!(test_hash_set_u32, std::collections::HashSet<u32>);
288    test!(test_b_tree_set_u32, std::collections::BTreeSet<u32>);
289    test!(test_linked_list_u32, std::collections::LinkedList<u32>);
290    test!(test_vec_deque_u32, std::collections::VecDeque<u32>);
291    test!(test_binary_heap_u32, std::collections::BinaryHeap<u32>);
292    #[cfg(feature = "chrono")]
293    test!(test_date_time_local, chrono::DateTime<chrono::Local>);
294    #[cfg(feature = "url")]
295    test!(test_url, url::Url);
296    #[cfg(feature = "uuid")]
297    test!(test_uuid, uuid::Uuid);
298    #[cfg(feature = "indexmap")]
299    test!(test_index_map_u32_u32, indexmap::IndexMap<u32, u32>);
300    #[cfg(feature = "indexmap")]
301    test!(test_index_set_u32, indexmap::IndexSet<u32>);
302}