packdir 0.1.0

Easily embed directories of binary file data
Documentation
use packdir::pack::*;
use packdir::*;
use std::path::PathBuf;

#[test]
fn test_pack_name_validation() {
    assert!(PackedDir::is_valid_packed_name("assets"));
    assert!(PackedDir::is_valid_packed_name("my_bundle"));
    assert!(PackedDir::is_valid_packed_name("bundle-name"));
    assert!(PackedDir::is_valid_packed_name("bundle123"));
    assert!(PackedDir::is_valid_packed_name("a"));
    assert!(PackedDir::is_valid_packed_name("A"));
    assert!(PackedDir::is_valid_packed_name("Bundle_Name-123"));

    assert!(!PackedDir::is_valid_packed_name(""));
    assert!(!PackedDir::is_valid_packed_name("123bundle"));
    assert!(!PackedDir::is_valid_packed_name("bundle.name"));
    assert!(!PackedDir::is_valid_packed_name("bundle name"));
    assert!(!PackedDir::is_valid_packed_name("bundle@name"));
    assert!(!PackedDir::is_valid_packed_name("bundle/name"));
    assert!(!PackedDir::is_valid_packed_name("bundle\\name"));
}

#[test]
fn test_file_to_pack_normalize_path() {
    let base_dir = PathBuf::from("/base/dir");
    let file_path = PathBuf::from("/base/dir/subdir/file.txt");

    let normalized = FileToPack::normalize_path_for_archive(&file_path, &base_dir);
    assert_eq!(normalized, PathBuf::from("subdir/file.txt"));

    // Test with file outside base (should return filename)
    let outside_file = PathBuf::from("/other/dir/file.txt");
    let normalized_outside = FileToPack::normalize_path_for_archive(&outside_file, &base_dir);
    assert_eq!(normalized_outside, PathBuf::from("file.txt"));

    // Test with file that has no filename (directory path)
    let no_filename = PathBuf::from("/other/dir/");
    let normalized_no_name = FileToPack::normalize_path_for_archive(&no_filename, &base_dir);
    assert_eq!(normalized_no_name, PathBuf::from("unknown"));
}

#[test]
fn test_compute_header_empty() {
    let files = vec![];
    let header = compute_header(&files).unwrap();

    // Should have: SIG(8) + VERSION(1) + num_files(4) = 13 bytes
    assert_eq!(header.len(), 13);

    // Verify header structure
    assert_eq!(&header[0..8], b"PACK DIR");
    assert_eq!(header[8], VERSION);

    let num_files = u32::from_le_bytes([header[9], header[10], header[11], header[12]]);
    assert_eq!(num_files, 0);
}

#[test]
fn test_compute_header_with_files() {
    // Create mock files for header computation
    let file1 = FileToPack {
        path: PathBuf::from("test.txt"),
        path_str: "test.txt".to_string(),
        unpacked_size: 100,
        packed_size: 80,
        packed_data: vec![0; 80],
        packer: Packer::Raw,
    };

    let file2 = FileToPack {
        path: PathBuf::from("dir/nested.txt"),
        path_str: "dir/nested.txt".to_string(),
        unpacked_size: 200,
        packed_size: 150,
        packed_data: vec![0; 150],
        packer: Packer::Gzip(Gzip::Level6),
    };

    let files = vec![file1, file2];
    let header = compute_header(&files).unwrap();

    // Verify header starts correctly
    assert_eq!(&header[0..8], b"PACK DIR");
    assert_eq!(header[8], VERSION);

    let num_files = u32::from_le_bytes([header[9], header[10], header[11], header[12]]);
    assert_eq!(num_files, 2);

    // Header should contain file entries
    assert!(header.len() > 13); // More than just basic header
}

#[test]
fn test_compute_header_too_many_files() {
    // Test with maximum number of files
    let files = vec![];
    let header = compute_header(&files).unwrap();
    assert!(header.len() >= 13);

    // We can't easily test the actual limit without creating billions of files,
    // but we can verify the function doesn't panic with reasonable inputs
}

#[test]
fn test_file_to_pack_path_normalization_edge_cases() {
    // Test Windows-style paths (results may vary by platform)
    let base = PathBuf::from("C:\\base");
    let file = PathBuf::from("C:\\base\\sub\\file.txt");
    let normalized = FileToPack::normalize_path_for_archive(&file, &base);
    // Should work on both Windows and Unix
    assert!(normalized.to_string_lossy().contains("file.txt"));

    // Test with same path (should return "unknown" since it's a directory)
    let same_path = PathBuf::from("/base/dir/file.txt");
    let normalized_same = FileToPack::normalize_path_for_archive(&same_path, &same_path);
    assert_eq!(normalized_same, PathBuf::from("unknown"));

    // Test with directory path (no filename)
    let dir_path = PathBuf::from("/other/dir/");
    let normalized_dir = FileToPack::normalize_path_for_archive(&dir_path, &PathBuf::from("/base"));
    assert_eq!(normalized_dir, PathBuf::from("unknown"));
}

#[test]
fn test_signature_and_version_constants() {
    assert_eq!(SIG, b"PACK DIR");
    assert_eq!(VERSION, 1);
    assert_eq!(SIG.len(), 8);
}

#[test]
fn test_packer_variations() {
    let test_data = b"test data for packing";

    let packers = vec![
        Packer::Raw,
        Packer::Gzip(Gzip::Level1),
        Packer::Gzip(Gzip::Level6),
        Packer::Gzip(Gzip::Level9),
        Packer::Xz(Xz::Level1),
        Packer::Xz(Xz::Level6),
        Packer::Xz(Xz::Level9),
    ];

    for packer in packers {
        let packed = packer.pack(test_data).unwrap();
        let unpacked = packer.unpack(&packed).unwrap();
        assert_eq!(unpacked, test_data);
    }
}