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
#![cfg(feature = "zip")]

use {
    crate::ExtractError,
    rc_zip::{
        prelude::*,
        EntryContents,
    },
    std::{
        fs,
        io,
        path::Path,
    },
};

impl From<rc_zip::Error> for ExtractError {
    #[inline]
    fn from(err: rc_zip::Error) -> ExtractError {
        ExtractError::ZipError(err)
    }
}

pub(crate) fn extract_zip<T: AsRef<Path> + ?Sized>(
    archive: &T,
    target: &Path
) -> Result<(), ExtractError> {
    let zipfile = fs::File::open(archive)?;
    let reader = zipfile.read_zip()?;
    for entry in reader.entries() {
        match entry.contents() {
            EntryContents::Directory(c) => {
                let path = target.join(c.entry.name());
                fs::create_dir_all(path.parent().unwrap())?;
            },
            EntryContents::File(c) => {
                let path = target.join(c.entry.name());
                fs::create_dir_all(path.parent().unwrap())?;
                let mut writer = fs::File::create(path)?;
                let mut reader = c
                    .entry
                    .reader(|offset| positioned_io::Cursor::new_pos(&zipfile, offset));

                io::copy(&mut reader, &mut writer)?;
            },
            // Symlinks aren't supported! Open an issue if you need them.

            _ => {}
        }
    }
    Ok(())
}

#[cfg(test)]
mod zip_tests {
    #[test]
    fn unzip() {
        use crate::tests::dir_list_equals;
        use crate::*;
        let cur_file = Path::new(file!());
        let root = cur_file.parent().unwrap().parent().unwrap();
        let out = root.join("target").join("test").join("unzip");
        let _ = fs::remove_dir_all(out.as_path());
        let data_dir = root.join("test_input");
        assert_eq!(extract_archive(data_dir.join("file.zip").as_path(), Some(out.as_path())).unwrap(), out);
        assert_eq!(true, dir_list_equals(out.join("zip").as_path(), vec![ "compressed.txt" ]));
        assert_eq!(
            fs::read_to_string(out.join("zip").join("compressed.txt")).unwrap(),
            "it works!".to_string()
        );
    }
}