1use anyhow::{anyhow, Result};
2use bincode::{Decode, Encode};
3use serde::{Deserialize, Serialize};
4
5use rucksack_lib::util;
6
7use crate::records;
8
9#[derive(Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq, Encode, Decode)]
10pub struct VersionedDB {
11 bytes: Vec<u8>,
12 version: String,
13}
14
15impl VersionedDB {
16 pub fn new(bytes: Vec<u8>, version: String) -> Result<VersionedDB> {
17 versions::SemVer::new(&version)
19 .ok_or_else(|| anyhow!("invalid version format '{}'", version))?;
20
21 Ok(VersionedDB { bytes, version })
22 }
23
24 pub fn deserialise(bytes: Vec<u8>) -> Result<VersionedDB> {
25 log::debug!(operation = "deserialise"; "Creating versioned DB from previously serialised versioned DB");
26 match bincode::decode_from_slice::<VersionedDB, _>(bytes.as_ref(), util::bincode_cfg()) {
27 Ok((result, _len)) => {
28 log::trace!(version = result.version.as_str(), operation = "deserialise"; "Deserialised versioned DB bytes");
29 Ok(result)
30 }
31 Err(e) => {
32 let msg = format!("couldn't deserialise versioned database file: {e:?}");
33 log::error!(error = e.to_string().as_str(), operation = "deserialise"; "{}", msg);
34 Err(anyhow!(msg))
35 }
36 }
37 }
38
39 pub fn from_bytes(bytes: Vec<u8>) -> Result<VersionedDB> {
40 log::debug!(operation = "init"; "Initialising versioned DB with encoded hashmap");
41 VersionedDB::new(bytes, records::version().to_string())
42 }
43
44 pub fn bytes(&self) -> Vec<u8> {
45 self.bytes.clone()
46 }
47
48 pub fn hash(&self) -> u32 {
49 crc32fast::hash(self.bytes.as_ref())
50 }
51
52 pub fn serialise(&self) -> Result<Vec<u8>> {
53 log::debug!(operation = "serialise"; "Serialising versioned DB");
54 match bincode::encode_to_vec(self, util::bincode_cfg()) {
55 Ok(bytes) => Ok(bytes),
56 Err(e) => {
57 let msg = format!("couldn't serialise versioned database ({e})");
58 log::error!(error = e.to_string().as_str(), operation = "serialise"; "{}", msg);
59 Err(anyhow!("{}", msg))
60 }
61 }
62 }
63
64 pub fn version(&self) -> versions::SemVer {
65 versions::SemVer::new(self.version.as_str()).expect("version validated in constructor")
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 #[test]
75 fn test_new_basic() {
76 let vsn_db = VersionedDB::new(vec![1, 2, 3], "1.0.0".to_string()).unwrap();
77 assert_eq!(vsn_db.bytes(), vec![1, 2, 3]);
78 assert_eq!(vsn_db.version(), versions::SemVer::new("1.0.0").unwrap());
79 }
80
81 #[test]
82 fn test_new_empty_bytes() {
83 let vsn_db = VersionedDB::new(vec![], "0.1.0".to_string()).unwrap();
84 assert_eq!(vsn_db.bytes(), vec![]);
85 assert_eq!(vsn_db.version(), versions::SemVer::new("0.1.0").unwrap());
86 }
87
88 #[test]
89 fn test_new_large_bytes() {
90 let large_vec = vec![42u8; 10000];
91 let vsn_db = VersionedDB::new(large_vec.clone(), "2.0.0".to_string()).unwrap();
92 assert_eq!(vsn_db.bytes(), large_vec);
93 }
94
95 #[test]
96 fn test_from_bytes() {
97 let bytes = vec![5, 10, 15];
98 let vsn_db = VersionedDB::from_bytes(bytes.clone()).unwrap();
99 assert_eq!(vsn_db.bytes(), bytes);
100 let _version = vsn_db.version();
102 }
103
104 #[test]
105 fn test_bytes_getter() {
106 let original = vec![1, 2, 3, 4, 5];
107 let vsn_db = VersionedDB::new(original.clone(), "1.0.0".to_string()).unwrap();
108 let retrieved = vsn_db.bytes();
109 assert_eq!(retrieved, original);
110 }
111
112 #[test]
113 fn test_hash_consistent() {
114 let vsn_db = VersionedDB::new(vec![1, 2, 3], "1.0.0".to_string()).unwrap();
115 let hash1 = vsn_db.hash();
116 let hash2 = vsn_db.hash();
117 assert_eq!(hash1, hash2, "Hash should be consistent");
118 }
119
120 #[test]
121 fn test_hash_different_for_different_bytes() {
122 let vsn_db1 = VersionedDB::new(vec![1, 2, 3], "1.0.0".to_string()).unwrap();
123 let vsn_db2 = VersionedDB::new(vec![4, 5, 6], "1.0.0".to_string()).unwrap();
124 assert_ne!(vsn_db1.hash(), vsn_db2.hash());
125 }
126
127 #[test]
128 fn test_hash_empty_bytes() {
129 let vsn_db = VersionedDB::new(vec![], "1.0.0".to_string()).unwrap();
130 let hash = vsn_db.hash();
131 assert_eq!(hash, 0); }
133
134 #[test]
135 fn test_hash_known_value() {
136 let vsn_db = VersionedDB::new(vec![2, 4, 16], "1.2.3".to_string()).unwrap();
137 assert_eq!(vsn_db.hash(), 2233391132);
138 }
139
140 #[test]
141 fn test_version_parsing() {
142 let vsn_db = VersionedDB::new(vec![1], "3.14.159".to_string()).unwrap();
143 let version = vsn_db.version();
144 assert_eq!(version.major, 3);
145 assert_eq!(version.minor, 14);
146 assert_eq!(version.patch, 159);
147 }
148
149 #[test]
150 fn test_version_with_prerelease() {
151 let vsn_db = VersionedDB::new(vec![1], "2.0.0-alpha.1".to_string()).unwrap();
152 let version = vsn_db.version();
153 assert_eq!(version.major, 2);
154 assert_eq!(version.minor, 0);
155 assert_eq!(version.patch, 0);
156 }
157
158 #[test]
159 fn test_version_comparisons() {
160 let vsn_db = VersionedDB::new(vec![1, 2], "1.5.0".to_string()).unwrap();
161 assert!(vsn_db.version() > versions::SemVer::new("1.4.9").unwrap());
162 assert!(vsn_db.version() < versions::SemVer::new("1.5.1").unwrap());
163 assert_eq!(vsn_db.version(), versions::SemVer::new("1.5.0").unwrap());
164 }
165
166 #[test]
167 fn test_serialise_basic() {
168 let vsn_db = VersionedDB::new(vec![10, 20], "1.0.0".to_string()).unwrap();
169 let result = vsn_db.serialise();
170 assert!(result.is_ok());
171 let bytes = result.unwrap();
172 assert!(!bytes.is_empty());
173 }
174
175 #[test]
176 fn test_serialise_empty() {
177 let vsn_db = VersionedDB::new(vec![], "0.0.1".to_string()).unwrap();
178 let result = vsn_db.serialise();
179 assert!(result.is_ok());
180 }
181
182 #[test]
183 fn test_deserialise_basic() {
184 let original = VersionedDB::new(vec![1, 2, 3], "1.2.3".to_string()).unwrap();
185 let serialised = original.serialise().unwrap();
186 let deserialised = VersionedDB::deserialise(serialised).unwrap();
187 assert_eq!(deserialised.bytes(), original.bytes());
188 assert_eq!(deserialised.version(), original.version());
189 }
190
191 #[test]
192 fn test_deserialise_invalid_bytes() {
193 let invalid_bytes = vec![255, 255, 255, 1, 2, 3];
194 let result = VersionedDB::deserialise(invalid_bytes);
195 assert!(result.is_err(), "Should fail to deserialise invalid bytes");
196 }
197
198 #[test]
199 fn test_deserialise_empty_bytes() {
200 let result = VersionedDB::deserialise(vec![]);
201 assert!(result.is_err(), "Should fail to deserialise empty bytes");
202 }
203
204 #[test]
205 fn test_deserialise_truncated_bytes() {
206 let original = VersionedDB::new(vec![1, 2, 3], "1.0.0".to_string()).unwrap();
207 let serialised = original.serialise().unwrap();
208 let truncated = &serialised[..serialised.len() / 2];
210 let result = VersionedDB::deserialise(truncated.to_vec());
211 assert!(result.is_err(), "Truncated data should fail to deserialise");
212 if let Err(e) = result {
213 assert!(e.to_string().contains("deserialise"));
214 }
215 }
216
217 #[test]
218 fn test_serialise_deserialise_roundtrip() {
219 let original = VersionedDB::new(vec![5, 10, 15, 20], "2.1.0".to_string()).unwrap();
220 let serialised = original.serialise().unwrap();
221 let deserialised = VersionedDB::deserialise(serialised).unwrap();
222
223 assert_eq!(original.bytes(), deserialised.bytes());
224 assert_eq!(original.version(), deserialised.version());
225 assert_eq!(original.hash(), deserialised.hash());
226 }
227
228 #[test]
229 fn test_serialise_deserialise_large_data() {
230 let large_data = vec![123u8; 5000];
231 let original = VersionedDB::new(large_data, "3.0.0".to_string()).unwrap();
232 let serialised = original.serialise().unwrap();
233 let deserialised = VersionedDB::deserialise(serialised).unwrap();
234
235 assert_eq!(original.bytes(), deserialised.bytes());
236 assert_eq!(original.hash(), deserialised.hash());
237 }
238
239 #[test]
240 fn test_clone() {
241 let original = VersionedDB::new(vec![1, 2, 3], "1.0.0".to_string()).unwrap();
242 let cloned = original.clone();
243 assert_eq!(original.bytes(), cloned.bytes());
244 assert_eq!(original.version(), cloned.version());
245 assert_eq!(original.hash(), cloned.hash());
246 }
247
248 #[test]
249 fn test_equality() {
250 let db1 = VersionedDB::new(vec![1, 2, 3], "1.0.0".to_string()).unwrap();
251 let db2 = VersionedDB::new(vec![1, 2, 3], "1.0.0".to_string()).unwrap();
252 let db3 = VersionedDB::new(vec![1, 2, 3], "1.0.1".to_string()).unwrap();
253 let db4 = VersionedDB::new(vec![1, 2, 4], "1.0.0".to_string()).unwrap();
254
255 assert_eq!(db1, db2);
256 assert_ne!(db1, db3); assert_ne!(db1, db4); }
259
260 #[test]
261 fn db_bytes() {
262 let vsn_db = VersionedDB::new(vec![2, 4, 16], "1.2.3".to_string()).unwrap();
263 assert!(vsn_db.version() > versions::SemVer::new("0.3.0").unwrap());
264 assert_eq!(vsn_db.hash(), 2233391132);
265 assert_eq!(vsn_db.version(), versions::SemVer::new("1.2.3").unwrap());
266 assert!(vsn_db.version() < versions::SemVer::new("2.3.0").unwrap());
267 let encoded = vsn_db.serialise().unwrap();
268 let expected = vec![
269 3, 0, 0, 0, 0, 0, 0, 0, 2, 4, 16, 5, 0, 0, 0, 0, 0, 0, 0, 49, 46, 50, 46, 51,
270 ];
271 assert_eq!(encoded, expected);
272 }
273}