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
use zip::Zip;
use errors::Result;
use errors::ResultExt;
use std::io;
use std::path::Path;
use std::path::PathBuf;
use std::io::Read;
use std::io::Write;
use std::fs;
use std::fs::DirBuilder;
use std::fs::File;
use std::process::Command;
use tempdir::TempDir;
pub struct ZipCommand {
command: String,
temp_dir: TempDir,
files: Vec<PathBuf>,
}
impl ZipCommand {
pub fn new() -> Result<ZipCommand> {
let temp_dir = TempDir::new("epub")
.chain_err(|| "could not create temporary directory")?;
let zip = ZipCommand {
command: String::from("zip"),
temp_dir: temp_dir,
files: vec!()
};
Ok(zip)
}
pub fn new_in<P: AsRef<Path>>(temp_path: P) -> Result<ZipCommand> {
let temp_dir = TempDir::new_in(temp_path, "epub")
.chain_err(|| "could not create temporary directory")?;
let zip = ZipCommand {
command: String::from("zip"),
temp_dir: temp_dir,
files: vec!()
};
Ok(zip)
}
pub fn command<S: Into<String>>(&mut self, command: S) -> &mut Self {
self.command = command.into();
self
}
}
impl Zip for ZipCommand {
fn write_file<P:AsRef<Path>, R: Read>(&mut self, path: P, mut content: R) -> Result<()> {
let path = path.as_ref();
if path.starts_with("..") || path.is_absolute() {
bail!("file {file} refers to a path outside the temporary directory. This is verbotten!");
}
let dest_file = self.temp_dir.path().join(path);
let dest_dir = dest_file.parent().unwrap();
if !fs::metadata(dest_dir).is_ok() {
DirBuilder::new()
.recursive(true)
.create(&dest_dir)
.chain_err(|| format!("could not create temporary directory in {path}",
path = dest_dir.display()))?;
}
let mut f = File::create(&dest_file)
.chain_err(|| format!("could not write to temporary file {file}",
file = path.display()))?;
io::copy(&mut content, &mut f)
.chain_err(|| format!("could not write to temporary file {file}",
file = path.display()))?;
self.files.push(path.to_path_buf());
Ok(())
}
fn generate<W: Write>(&mut self, mut to: W) -> Result<()> {
let mut command = Command::new(&self.command);
command
.current_dir(self.temp_dir.path())
.arg("-X")
.arg("-");
for file in self.files.iter() {
command.arg(format!("{}", file.display()));
}
let output = command.output()
.chain_err(|| format!("failed to run command {name}",
name = self.command))?;
if output.status.success() {
to.write_all(output.stdout.as_ref())
.chain_err(|| "error writing result of the zip command")?;
Ok(())
} else {
bail!("command {name} didn't return succesfully: {output}",
name = self.command,
output = String::from_utf8_lossy(&output.stderr));
}
}
}