File: unzip.rs

package info (click to toggle)
rust-piz 0.5.1-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 292 kB
  • sloc: sh: 14; makefile: 2
file content (79 lines) | stat: -rw-r--r-- 2,333 bytes parent folder | download
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
use std::fs::{self, File};
use std::io;
use std::path::PathBuf;

use anyhow::*;
use log::*;
use memmap2::Mmap;
use rayon::prelude::*;
use structopt::*;

use piz::read::*;

#[derive(Debug, StructOpt)]
#[structopt(name = "unzip", about = "Dumps a .zip file into the current directory")]
struct Opt {
    /// Pass multiple times for additional verbosity (info, debug, trace)
    #[structopt(short, long, parse(from_occurrences))]
    verbosity: usize,

    /// Change to the given directory before perfoming any operations.
    #[structopt(short = "C", long)]
    directory: Option<PathBuf>,

    /// Prints the tree of files in the ZIP archive instead of extracting them.
    #[structopt(short = "n", long)]
    dry_run: bool,

    #[structopt(name("ZIP file"))]
    zip_path: PathBuf,
}

fn main() -> Result<()> {
    let args = Opt::from_args();

    let mut errlog = stderrlog::new();
    errlog.verbosity(args.verbosity + 1);
    errlog.init()?;

    if let Some(chto) = args.directory {
        std::env::set_current_dir(&chto)
            .with_context(|| format!("Couldn't set working directory to {}", chto.display()))?;
    }

    info!("Memory mapping {:#?}", &args.zip_path);
    let zip_file = File::open(&args.zip_path).context("Couldn't open zip file")?;
    let mapping = unsafe { Mmap::map(&zip_file).context("Couldn't mmap zip file")? };

    let archive = ZipArchive::with_prepended_data(&mapping)
        .context("Couldn't load archive")?
        .0;
    let tree = as_tree(archive.entries())?;

    if args.dry_run {
        print_tree(&tree)
    } else {
        read_zip(&tree, &archive)
    }
}

fn print_tree(tree: &DirectoryContents) -> Result<()> {
    for entry in tree.traverse() {
        println!("{}", entry.metadata().path);
    }
    Ok(())
}

fn read_zip(tree: &DirectoryContents, archive: &ZipArchive) -> Result<()> {
    tree.files().par_bridge().try_for_each(|entry| {
        if let Some(parent) = entry.path.parent() {
            fs::create_dir_all(parent)
                .with_context(|| format!("Couldn't create directory {}", parent))?;
        }
        let mut reader = archive.read(entry)?;
        let mut sink = File::create(&*entry.path)
            .with_context(|| format!("Couldn't create file {}", entry.path))?;
        io::copy(&mut reader, &mut sink)?;
        Ok(())
    })
}