files_diff/compress.rs
1use rkyv::Archive;
2use std::io::{Read, Write};
3
4use crate::Error;
5
6const ZSTD_COMPRESSION_LEVEL: i32 = 21;
7
8/// Compression algorithms available for patch data.
9///
10/// Determines how patch data is compressed before storage or transmission.
11/// Different algorithms offer tradeoffs between compression ratio and speed.
12///
13/// # Example
14/// ```rust
15/// use files_diff::{diff, DiffAlgorithm, CompressAlgorithm};
16///
17/// let before = b"original content";
18/// let after = b"modified content";
19///
20/// // Use no compression for debugging or when speed is critical
21/// let uncompressed = diff(
22/// before,
23/// after,
24/// DiffAlgorithm::Rsync020,
25/// CompressAlgorithm::None
26/// )?;
27///
28/// // Use Zstd for maximum compression
29/// let compressed = diff(
30/// before,
31/// after,
32/// DiffAlgorithm::Rsync020,
33/// CompressAlgorithm::Zstd
34/// )?;
35/// # Ok::<(), files_diff::Error>(())
36/// ```
37#[derive(Archive, rkyv::Deserialize, rkyv::Serialize, Debug, PartialEq, Copy, Clone, Eq, Hash)]
38#[rkyv(derive(Debug, PartialEq, Copy, Clone))]
39pub enum CompressAlgorithm {
40 /// No compression. Patch data is stored as-is.
41 /// Use this when:
42 /// - Debugging patches
43 /// - Working with already compressed data
44 /// - Speed is more important than size
45 None,
46
47 /// Zstandard compression with level 21 (maximum compression).
48 /// Use this when:
49 /// - Minimizing patch size is critical
50 /// - Network bandwidth or storage is limited
51 /// - Compression time is not a concern
52 Zstd,
53}
54
55impl std::fmt::Display for CompressAlgorithm {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 write!(f, "{:?}", self)
58 }
59}
60
61impl CompressAlgorithm {
62 /// Compresses the input data using the selected algorithm.
63 pub fn compress(self, input: &[u8]) -> Result<Vec<u8>, Error> {
64 match self {
65 Self::None => Ok(input.to_vec()),
66 Self::Zstd => {
67 let mut encoder =
68 zstd::Encoder::new(Vec::new(), ZSTD_COMPRESSION_LEVEL).map_err(|e| {
69 Error::ZipError(format!("failed to create zstd encoder: {}", e))
70 })?;
71 encoder
72 .write_all(input)
73 .map_err(|e| Error::ZipError(format!("failed to write: {}", e)))?;
74 Ok(encoder
75 .finish()
76 .map_err(|e| Error::ZipError(format!("failed to finish: {}", e)))?)
77 }
78 }
79 }
80
81 /// Decompresses the input data using the selected algorithm.
82 pub(crate) fn decompress(self, input: &[u8]) -> Result<Vec<u8>, Error> {
83 match self {
84 Self::None => Ok(input.to_vec()),
85 Self::Zstd => {
86 let mut output = Vec::new();
87 let mut decoder = zstd::Decoder::new(input).map_err(|e| {
88 Error::ZipError(format!("failed to create zstd decoder: {}", e))
89 })?;
90 decoder
91 .read_to_end(&mut output)
92 .map_err(|e| Error::ZipError(format!("failed to read: {}", e)))?;
93 Ok(output)
94 }
95 }
96 }
97}