#![forbid(unsafe_code)]
#![forbid(missing_docs)]
use super::crc32;
use super::Messages;
use anyhow::{
anyhow,
Result,
};
use std::fs;
use std::io::{
self,
Read,
Seek,
};
use std::path::{
Path,
PathBuf,
};
use tempfile::{
NamedTempFile,
TempPath,
};
use zip::{
read::ZipFile,
ZipArchive,
};
#[cfg(target_family = "unix")]
use std::fs::Permissions;
#[cfg(target_family = "unix")]
use std::os::unix::fs::PermissionsExt;
pub fn bin_dir() -> Result<PathBuf> {
if let Some(dir) = dirs::executable_dir() {
if !dir.exists() {
fs::create_dir_all(&dir)?;
}
Ok(dir)
}
else {
let msg = concat!(
"Could not find suitable install-dir.\n",
"Consider passing --install-dir to manually specify",
);
Err(anyhow!(msg))
}
}
fn extract(mut zipfile: &mut ZipFile, dir: &Path) -> Result<TempPath> {
let mut tmpfile = NamedTempFile::new_in(&dir)?;
io::copy(&mut zipfile, &mut tmpfile)?;
let tmpfile = tmpfile.into_temp_path();
let expected = zipfile.crc32();
crc32::check(&tmpfile, expected)?;
Ok(tmpfile)
}
pub fn install<F>(messages: &Messages, zipfile: &mut F, dir: &Path) -> Result<()>
where
F: Read + Seek,
{
if !dir.is_dir() {
let err = anyhow!(
"install: Destination '{}' is not a directory",
dir.display(),
);
return Err(err);
}
let mut zip = ZipArchive::new(zipfile).expect("new ziparchive");
for i in 0..zip.len() {
let mut file = zip.by_index(i)?;
let filename = file.name();
let basename = Path::new(filename).file_name()
.ok_or_else(|| {
anyhow!("Couldn't get basename from: {}", filename)
})?;
let filename = Path::new(basename).to_path_buf();
messages.extracting_file(&filename, dir);
let tmpfile = extract(&mut file, dir)?;
let dest = dir.join(&filename);
tmpfile.persist(&dest)?;
#[cfg(target_family = "unix")]
if let Some(mode) = file.unix_mode() {
fs::set_permissions(&dest, Permissions::from_mode(mode))?;
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::File;
use std::path::Path;
#[test]
fn test_install_dir_not_dir() {
let test_file = concat!(
env!("CARGO_MANIFEST_DIR"),
"/test-data/test.txt",
);
let mut file = File::open(&test_file).unwrap();
let dest = Path::new(test_file).to_path_buf();
let messages = Messages::new(false);
let res = install(&messages, &mut file, &dest);
assert!(res.is_err());
}
}