Skip to main content

easy_archive/archive/tar/
encode.rs

1/// TAR encoding implementation
2use crate::{
3    File,
4    error::{ArchiveError, Result},
5    traits::Encode,
6    utils::check_duplicate_files,
7};
8
9use super::Tar;
10
11/// Common helper function for encoding TAR archives
12///
13/// This function handles the core TAR encoding logic that is shared across
14/// all TAR format variants. It includes duplicate file detection.
15///
16/// # Performance Notes
17/// - Checks for duplicates before encoding to fail fast
18/// - Uses buffered writing for better I/O performance
19/// - Pre-allocates buffer based on estimated archive size
20///
21/// # Arguments
22/// * `files` - The files to include in the archive
23///
24/// # Returns
25/// * `Ok(Vec<u8>)` - The encoded TAR data
26/// * `Err(ArchiveError)` - If encoding fails or duplicates are detected
27pub(crate) fn encode_tar_archive(files: Vec<File>) -> Result<Vec<u8>> {
28    // Check for duplicate files before encoding (fail fast)
29    check_duplicate_files(&files)?;
30
31    // Pre-allocate buffer with estimated size (sum of file sizes + 512 bytes per file for headers)
32    let estimated_size: usize = files.iter().map(|f| f.buffer.len() + 512).sum();
33    let mut buffer: Vec<u8> = Vec::with_capacity(estimated_size);
34    {
35        let mut builder = tar::Builder::new(&mut buffer);
36
37        for file in files {
38            let mut header = tar::Header::new_gnu();
39            header.set_size(file.buffer.len() as u64);
40            header.set_mode(file.mode.unwrap_or(0o644));
41            header.set_uid(0);
42            header.set_gid(0);
43            header.set_mtime(file.last_modified.unwrap_or(0));
44            header.set_cksum();
45
46            builder
47                .append_data(&mut header, &file.path, &file.buffer[..])
48                .map_err(|e| ArchiveError::EncodeFailed {
49                    format: "tar".to_string(),
50                    reason: format!("Failed to append file '{}': {}", file.path, e),
51                })?;
52        }
53
54        builder.finish().map_err(|e| ArchiveError::EncodeFailed {
55            format: "tar".to_string(),
56            reason: format!("Failed to finalize tar archive: {}", e),
57        })?;
58    }
59
60    Ok(buffer)
61}
62
63impl Encode for Tar {
64    fn encode(files: Vec<File>) -> Result<Vec<u8>> {
65        encode_tar_archive(files)
66    }
67}