megacommerce_shared/models/
files.rs1use base64::{engine::general_purpose::STANDARD, Engine as _};
2use regex::Regex;
3use sha2::{Digest, Sha256};
4
5use super::errors::{ErrorType, SimpleError};
6
7pub enum UnitSizeType {
8 Bytes,
9 KB,
10 MB,
11 GB,
12}
13
14impl UnitSizeType {
15 pub const fn as_str(&self) -> &'static str {
16 match self {
17 Self::Bytes => "Bytes",
18 Self::KB => "Kb",
19 Self::MB => "Mb",
20 Self::GB => "Gb",
21 }
22 }
23}
24
25pub fn validate_base64_checksum(
26 base64_string: &str,
27 expected_checksum: &str,
28 pre_decoded_data: Option<Vec<u8>>,
29) -> Result<bool, SimpleError> {
30 let data = match pre_decoded_data {
31 Some(dec) => dec,
32 None => {
33 let re = Regex::new(r"^data:[^;]+;base64,").map_err(|err| SimpleError {
35 err: Box::new(err),
36 message: "failed to create a new regex".to_string(),
37 _type: ErrorType::RegexInvalid,
38 })?;
39 let base64_data = re.replace(base64_string, "");
40 STANDARD.decode(base64_data.as_bytes()).map_err(|err| SimpleError {
41 err: Box::new(err),
42 message: "invalid base64 data string".to_string(),
43 _type: ErrorType::Base64Invalid,
44 })?
45 }
46 };
47
48 let mut hasher = Sha256::new();
49 hasher.update(&data);
50 let hash_result = hasher.finalize();
51
52 let computed_checksum = hash_result.iter().map(|b| format!("{:02x}", b)).collect::<String>();
53
54 Ok(computed_checksum == expected_checksum)
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60 use base64::{engine::general_purpose::STANDARD, Engine};
61
62 fn get_test_data() -> (String, String, Vec<u8>) {
64 let base64_data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==";
66
67 let decoded_data = STANDARD.decode(base64_data).unwrap();
69 let mut hasher = Sha256::new();
70 hasher.update(&decoded_data);
71 let hash_result = hasher.finalize();
72 let actual_hash = hash_result.iter().map(|b| format!("{:02x}", b)).collect::<String>();
73
74 (base64_data.to_string(), actual_hash, decoded_data)
75 }
76
77 #[test]
78 fn test_valid_checksum_without_pre_decoded() {
79 let (base64, expected_hash, _) = get_test_data();
80
81 let result = validate_base64_checksum(&base64, &expected_hash, None).unwrap();
82
83 assert!(result);
84 }
85
86 #[test]
87 fn test_valid_checksum_with_pre_decoded() {
88 let (base64, expected_hash, decoded_data) = get_test_data();
89
90 let result = validate_base64_checksum(&base64, &expected_hash, Some(decoded_data)).unwrap();
91
92 assert!(result);
93 }
94
95 #[test]
96 fn test_invalid_checksum() {
97 let (base64, expected_hash, _) = get_test_data();
98 let wrong_hash = "0000000000000000000000000000000000000000000000000000000000000000";
99
100 let result = validate_base64_checksum(&base64, wrong_hash, None).unwrap();
101
102 assert!(!result);
103 }
104
105 #[test]
106 fn test_data_url_without_pre_decoded() {
107 let (base64, expected_hash, _) = get_test_data();
108 let data_url = format!("data:image/png;base64,{}", base64);
109
110 let result = validate_base64_checksum(&data_url, &expected_hash, None).unwrap();
111
112 assert!(result);
113 }
114
115 #[test]
116 fn test_data_url_with_pre_decoded() {
117 let (base64, expected_hash, decoded_data) = get_test_data();
118 let data_url = format!("data:image/png;base64,{}", base64);
119
120 let result = validate_base64_checksum(&data_url, &expected_hash, Some(decoded_data)).unwrap();
121
122 assert!(result);
123 }
124
125 #[test]
126 fn test_different_data_url_mime_types() {
127 let (base64, expected_hash, _) = get_test_data();
128 let mime_types = vec![
129 "data:image/jpeg;base64,",
130 "data:application/octet-stream;base64,",
131 "data:text/plain;base64,",
132 ];
133
134 for mime in mime_types {
135 let data_url = format!("{}{}", mime, base64);
136 let result = validate_base64_checksum(&data_url, &expected_hash, None).unwrap();
137 assert!(result, "Failed with MIME type: {}", mime);
138 }
139 }
140
141 #[test]
142 fn test_invalid_base64() {
143 let invalid_base64 = "invalid!!!base64@@@data";
144
145 let result = validate_base64_checksum(invalid_base64, "some_hash", None);
146
147 assert!(result.is_err());
148 if let Err(err) = result {
149 assert_eq!(err._type, ErrorType::Base64Invalid);
150 }
151 }
152
153 #[test]
154 fn test_empty_base64_string() {
155 let empty_hash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; let result = validate_base64_checksum("", empty_hash, None).unwrap();
158 assert!(result);
159 }
160
161 #[test]
162 fn test_pre_decoded_takes_precedence() {
163 let (base64, expected_hash, _) = get_test_data();
164
165 let different_data = b"completely different data".to_vec();
167
168 let result = validate_base64_checksum(&base64, &expected_hash, Some(different_data)).unwrap();
170
171 assert!(!result); }
173
174 #[test]
175 fn test_consistency_between_with_and_without_pre_decoded() {
176 let (base64, expected_hash, decoded_data) = get_test_data();
177
178 let result1 = validate_base64_checksum(&base64, &expected_hash, None).unwrap();
179 let result2 = validate_base64_checksum(&base64, &expected_hash, Some(decoded_data)).unwrap();
180
181 assert_eq!(result1, result2);
182 assert!(result1);
183 assert!(result2);
184 }
185
186 #[test]
188 fn test_known_hash_value() {
189 let base64_data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==";
190 let expected_hash = "1c30f1a2686fbf86c76c9115b471df8c0b6b9c5b6c5e2c7b8c5c6c5c6c5c6c5c6c5"; let decoded = STANDARD.decode(base64_data).unwrap();
194 let mut hasher = Sha256::new();
195 hasher.update(&decoded);
196 let actual_hash = hasher.finalize();
197 let actual_hash_hex = actual_hash.iter().map(|b| format!("{:02x}", b)).collect::<String>();
198
199 println!("Actual hash for test data: {}", actual_hash_hex);
200
201 let result = validate_base64_checksum(base64_data, &actual_hash_hex, None).unwrap();
203 assert!(result);
204 }
205}