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}