1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
mod compression;

use std::fs::File;
use std::io::{Error, ErrorKind, Read, Seek};

use crate::contents::enums;

fn find_subsequence(source: &[u8], target: &[u8]) -> Option<usize> {
    source
        .windows(target.len())
        .position(|window| window == target)
}

pub fn get_file_type(file_path: &std::path::PathBuf) -> Result<enums::FileType, std::io::Error> {
    struct CompressMagic {
        magic_number: &'static [u8],
        length: usize,
        file_type: enums::FileType,
    }

    let compress_magic_startswith_list = [
        CompressMagic {
            magic_number: b"BZh",
            length: 3,
            file_type: enums::FileType::Bz2,
        },
        CompressMagic {
            magic_number: &[0x1f, 0x8b],
            length: 2,
            file_type: enums::FileType::Gz,
        },
        CompressMagic {
            magic_number: &[0x50, 0x4b, 0x03, 0x04],
            length: 4,
            file_type: enums::FileType::Zip,
        },
    ];

    let compress_magic_include_list = [CompressMagic {
        magic_number: &[0x75, 0x73, 0x74, 0x61, 0x72],
        length: 5,
        file_type: enums::FileType::Tar,
    }];

    let mut startswith_buffer = [0u8; 4];
    let mut file = File::open(file_path).expect("File open failed");
    match file.read_exact(&mut startswith_buffer) {
        Ok(_) => (),
        Err(_) => return Err(Error::from(ErrorKind::Unsupported)),
    };

    for compress_magic in compress_magic_startswith_list.iter() {
        if startswith_buffer.get(..compress_magic.length).unwrap() == compress_magic.magic_number {
            return Ok(compress_magic.file_type);
        }
    }

    file.rewind().expect("Seek 0 failed");
    let mut include_vec = Vec::new();
    file.read_to_end(&mut include_vec)
        .expect("Read file failed");

    for compress_magic in compress_magic_include_list.iter() {
        if find_subsequence(&include_vec, compress_magic.magic_number).is_some() {
            return Ok(compress_magic.file_type);
        }
    }

    Err(Error::from(ErrorKind::Unsupported))
}

pub fn compress(
    file_type: enums::FileType,
    src_path: &std::path::PathBuf,
    dst_path: &std::path::PathBuf,
) {
    let src_path_str = src_path.to_owned().into_os_string().into_string().unwrap();
    let dst_path_str = dst_path.to_owned().into_os_string().into_string().unwrap();
    match file_type {
        enums::FileType::Zip => {
            compression::zip::compress(&src_path_str, &dst_path_str);
        }
        enums::FileType::Tar => {
            compression::tar::compress(&src_path_str, &dst_path_str);
        }
        enums::FileType::Bz2 => {
            compression::bz2::compress(&src_path_str, &dst_path_str);
        }
        enums::FileType::Gz => {
            compression::gz::compress(&src_path_str, &dst_path_str);
        }
        enums::FileType::Tarbz2 => {
            compression::tar_bz2::compress(&src_path_str, &dst_path_str);
        }
        enums::FileType::Targz => {
            compression::tar_gz::compress(&src_path_str, &dst_path_str);
        }
    }
}

pub fn decompress(
    file_type: enums::FileType,
    src_path: &std::path::PathBuf,
    dst_path: &std::path::PathBuf,
) {
    let src_path_str = src_path.to_owned().into_os_string().into_string().unwrap();
    let dst_path_str = dst_path.to_owned().into_os_string().into_string().unwrap();
    match file_type {
        enums::FileType::Zip => {
            println!("zip");
            compression::zip::decompress(&src_path_str, &dst_path_str);
        }
        enums::FileType::Tar => {
            println!("tar");
            compression::tar::decompress(&src_path_str, &dst_path_str);
        }
        enums::FileType::Tarbz2 => {
            println!("tar.bz2");
            compression::tar_bz2::decompress(&src_path_str, &dst_path_str);
        }
        enums::FileType::Targz => {
            println!("tar.gz");
            compression::tar_gz::decompress(&src_path_str, &dst_path_str);
        }
        enums::FileType::Bz2 => {
            println!("bz2");
            compression::bz2::decompress(&src_path_str, &dst_path_str);
        }
        enums::FileType::Gz => {
            println!("gz");
            compression::gz::decompress(&src_path_str, &dst_path_str);
        }
    }
}