Mountain/IPC/Encryption/
MessageCompressor.rs1use std::io::{Read, Write};
36
37use flate2::{Compression, read::GzDecoder, write::GzEncoder};
38
39use super::super::Message::TauriIPCMessage;
40use crate::dev_log;
41
42pub struct MessageCompressor {
108 CompressionLevel:u32,
110
111 BatchSize:usize,
113}
114
115impl MessageCompressor {
116 pub fn new(CompressionLevel:u32, BatchSize:usize) -> Self {
128 dev_log!(
129 "encryption",
130 "[MessageCompressor] Created with level: {}, batch size: {}",
131 CompressionLevel,
132 BatchSize
133 );
134 Self { CompressionLevel, BatchSize }
135 }
136
137 pub fn compress_messages(&self, Messages:Vec<TauriIPCMessage>) -> Result<Vec<u8>, String> {
156 dev_log!("encryption", "[MessageCompressor] Compressing {} messages", Messages.len());
157
158 let SerializedMessages =
160 serde_json::to_vec(&Messages).map_err(|e| format!("Failed to serialize messages: {}", e))?;
161
162 let original_size = SerializedMessages.len();
163
164 let mut encoder = GzEncoder::new(Vec::new(), Compression::new(self.CompressionLevel));
166 encoder
167 .write_all(&SerializedMessages)
168 .map_err(|e| format!("Failed to compress messages: {}", e))?;
169
170 let compressed_data = encoder.finish().map_err(|e| format!("Failed to finish compression: {}", e))?;
171
172 let compressed_size = compressed_data.len();
173 let ratio = if original_size > 0 {
174 (compressed_size as f64 / original_size as f64) * 100.0
175 } else {
176 100.0
177 };
178
179 dev_log!(
180 "encryption",
181 "[MessageCompressor] Compression complete: {} -> {} bytes ({:.1}%)",
182 original_size,
183 compressed_size,
184 ratio
185 );
186
187 Ok(compressed_data)
188 }
189
190 pub fn decompress_messages(&self, CompressedData:&[u8]) -> Result<Vec<TauriIPCMessage>, String> {
208 dev_log!("encryption", "[MessageCompressor] Decompressing {} bytes", CompressedData.len());
209
210 let compressed_size = CompressedData.len();
211
212 let mut decoder = GzDecoder::new(CompressedData);
214 let mut DecompressedData = Vec::new();
215 decoder
216 .read_to_end(&mut DecompressedData)
217 .map_err(|e| format!("Failed to decompress data: {}", e))?;
218
219 let decompressed_size = DecompressedData.len();
220
221 let messages:Vec<TauriIPCMessage> =
223 serde_json::from_slice(&DecompressedData).map_err(|e| format!("Failed to deserialize messages: {}", e))?;
224
225 dev_log!(
226 "encryption",
227 "[MessageCompressor] Decompression complete: {} -> {} bytes, {} messages",
228 compressed_size,
229 decompressed_size,
230 messages.len()
231 );
232
233 Ok(messages)
234 }
235
236 pub fn should_batch(&self, MessagesCount:usize) -> bool {
258 let should_batch = MessagesCount >= self.BatchSize;
259 dev_log!(
260 "encryption",
261 "[MessageCompressor] Batch check: {} >= {} = {}",
262 MessagesCount,
263 self.BatchSize,
264 should_batch
265 );
266 should_batch
267 }
268
269 pub fn compression_level(&self) -> u32 { self.CompressionLevel }
271
272 pub fn batch_size(&self) -> usize { self.BatchSize }
274
275 pub fn default() -> Self { Self::new(6, 10) }
277
278 pub fn fast() -> Self { Self::new(3, 5) }
280
281 pub fn max() -> Self { Self::new(9, 20) }
283}
284
285#[cfg(test)]
286#[allow(unused_imports)]
287mod tests {
288 use super::*;
289
290 fn create_test_message(id:u32) -> TauriIPCMessage {
291 TauriIPCMessage::new(
292 format!("test_channel_{}", id),
293 serde_json::json!({
294 "id": id,
295 "data": "test data that should compress well when repeated many times across multiple messages".repeat(10)
296 }),
297 Some("test_sender".to_string()),
298 )
299 }
300
301 #[test]
302 fn test_compressor_creation() {
303 let compressor = MessageCompressor::new(6, 10);
304 assert_eq!(compressor.compression_level(), 6);
305 assert_eq!(compressor.batch_size(), 10);
306 }
307
308 #[test]
309 fn test_default_compressor() {
310 let compressor = MessageCompressor::default();
311 assert_eq!(compressor.compression_level(), 6);
312 assert_eq!(compressor.batch_size(), 10);
313 }
314
315 #[test]
316 fn test_fast_compressor() {
317 let compressor = MessageCompressor::fast();
318 assert_eq!(compressor.compression_level(), 3);
319 assert_eq!(compressor.batch_size(), 5);
320 }
321
322 #[test]
323 fn test_max_compressor() {
324 let compressor = MessageCompressor::max();
325 assert_eq!(compressor.compression_level(), 9);
326 assert_eq!(compressor.batch_size(), 20);
327 }
328
329 #[test]
330 fn test_should_batch() {
331 let compressor = MessageCompressor::new(6, 10);
332 assert!(!compressor.should_batch(5));
333 assert!(compressor.should_batch(10));
334 assert!(compressor.should_batch(15));
335 }
336
337 #[test]
338 fn test_compress_and_decompress() {
339 let compressor = MessageCompressor::default();
340 let original_messages = vec![create_test_message(1), create_test_message(2), create_test_message(3)];
341
342 let compressed = compressor.compress_messages(original_messages.clone()).unwrap();
344 assert!(!compressed.is_empty());
345
346 let decompressed = compressor.decompress_messages(&compressed).unwrap();
348 assert_eq!(decompressed.len(), original_messages.len());
349
350 for i in 0..original_messages.len() {
352 assert_eq!(decompressed[i].channel, original_messages[i].channel);
353 }
354 }
355
356 #[test]
357 fn test_compression_ratio() {
358 let compressor = MessageCompressor::default();
359
360 let messages:Vec<TauriIPCMessage> = (0..20).map(|i| create_test_message(i)).collect();
362
363 let compressed = compressor.compress_messages(messages.clone()).unwrap();
364
365 let original_data = serde_json::to_vec(&messages).unwrap();
367 assert!(compressed.len() < original_data.len());
368 }
369
370 #[test]
371 fn test_empty_messages() {
372 let compressor = MessageCompressor::default();
373 let messages = vec![];
374
375 let compressed = compressor.compress_messages(messages).unwrap();
376 let decompressed = compressor.decompress_messages(&compressed).unwrap();
377
378 assert!(decompressed.is_empty());
379 }
380
381 #[test]
382 fn test_single_message() {
383 let compressor = MessageCompressor::default();
384 let messages = vec![create_test_message(1)];
385
386 let compressed = compressor.compress_messages(messages.clone()).unwrap();
387 let decompressed = compressor.decompress_messages(&compressed).unwrap();
388
389 assert_eq!(decompressed.len(), 1);
390 assert_eq!(decompressed[0].channel, messages[0].channel);
391 }
392}