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
extern crate zip;
extern crate time;
extern crate transformation_pipeline;

mod transformation_metadata;

use std::io;
use std::io::prelude::Read;
use std::fs;
use std::path::{Path, PathBuf};
use transformation_pipeline::TransformationPipeline;
use transformation_metadata::ExtractionMetadata;

mod strip_components;
use strip_components::StripComponents;

pub struct UnzipperStats {

    dirs: u16,

    files: u16,

}

type UnzipperResult = Result<UnzipperStats, io::Error>;

pub struct Unzipper<R: Read + io::Seek, O: AsRef<Path>> {

    source: R,

    outdir: O,

    strip_components: u8,

}

impl<R: Read + io::Seek, O: AsRef<Path>> Unzipper<R, O> {

    pub fn new(reader: R, output: O) -> Unzipper<R, O> {
        Unzipper {
            source: reader,
            outdir: output,
            strip_components: 0,
        }
    }

    pub fn strip_components(mut self, num: u8) -> Unzipper<R, O> {
        self.strip_components = num;
        self
    }

    pub fn unzip(self) -> UnzipperResult {
        let mut archive = zip::ZipArchive::new(self.source)?;
        let outdir: &Path = Path::new(self.outdir.as_ref());

        let mut stats = UnzipperStats {
            dirs: 0,
            files: 0,
        };

        let pipeline: TransformationPipeline<ExtractionMetadata> = TransformationPipeline::new(vec![
            Box::new(StripComponents::new(self.strip_components)),
        ]);

        for i in 0..archive.len() {
            let mut file = archive.by_index(i)?;

            let metadata = pipeline.run(ExtractionMetadata {
                extract: true,
                filename: file.name().to_owned(),
                comment: file.comment().to_owned(),
                compressed_size: file.compressed_size(),
                uncompressed_size: file.size(),
                crc32: file.crc32(),
                data_start: file.data_start(),
                last_modified: file.last_modified(),
                unix_mode: file.unix_mode(),
            })?;

            if !metadata.extract {
                continue;
            }

            let outpath: PathBuf = outdir.join(metadata.filename);

            if let Some(parent_dir) = outpath.as_path().parent() {
                fs::create_dir_all(&parent_dir)?;
            }

            if (&*file.name()).ends_with('/') {
                stats.dirs = stats.dirs + 1;
                continue;
            }
            let mut outfile = fs::File::create(&outpath)?;
            io::copy(&mut file, &mut outfile)?;

            // TODO: Handle unix_mode, last_modified

            stats.files = stats.files + 1;
        }

        Ok(stats)
    }

}