use failure::Error;
use tarcrate;
use std::path::PathBuf;
use std::fs;
use std::io::Read;
#[cfg(feature = "indicate")]
extern crate indicatif;
pub fn extract(file : &PathBuf, des : &PathBuf) -> Result<PathBuf,Error> {
let mut buffer : Vec<u8> = Vec::new();
let mut archive = fs::File::open(&file)?;
archive.read_to_end(&mut buffer)?;
extract_buffer(&buffer,des,false)
}
pub fn extract_root(file : &PathBuf, des : &PathBuf) -> Result<PathBuf,Error> {
let mut buffer : Vec<u8> = Vec::new();
let mut archive = fs::File::open(&file)?;
archive.read_to_end(&mut buffer)?;
extract_buffer(&buffer,des,true)
}
pub fn contains(archive : &PathBuf, file : &str) -> Result<bool,Error> {
let mut buffer : Vec<u8> = Vec::new();
let mut archive_file = fs::File::open(&archive)?;
archive_file.read_to_end(&mut buffer)?;
buffer_contains(&buffer,file)
}
pub fn buffer_contains(buffer : &Vec<u8>, file_name : &str) -> Result<bool,Error> {
let mut archive = tarcrate::Archive::new(&buffer[..]);
for file in archive.entries()? {
let mut file = file?;
let filepath = file.header().path()?.to_str().unwrap().to_string();
if filepath.chars().last().unwrap() == "/".chars().last().unwrap() {
continue;
}
let filename = filepath.split("/").collect::<Vec<_>>();
if filename[filename.len()-1] == file_name {
return Ok(true);
}
}
Ok(false)
}
pub fn extract_buffer(buffer : &Vec<u8>, des : &PathBuf, root : bool) -> Result<PathBuf,Error> {
let mut archive = tarcrate::Archive::new(&buffer[..]);
let mut root_length : Option<usize> = None;
#[cfg(feature = "indicate")]
let bar = {
let bar = indicatif::ProgressBar::new_spinner();
bar.set_message(&format!("Extracting archive.."));
bar
};
if root {
#[cfg(feature = "indicate")]
let mut count = {
bar.set_message(&format!("Determining archive root, 0 files"));
0
};
for file in archive.entries()? {
let mut file = file?;
if file.header().path()?.to_str().unwrap().chars().last().unwrap() == "/".chars().last().unwrap() {
continue;
}
let mut length = 0;
if let Some(file_name) = file.header().path()?.to_str() {
#[cfg(feature = "indicate")]
{
count += 1;
bar.set_message(&format!("Determining archive root, {} files",count));
}
let splits = file_name.split("/").collect::<Vec<_>>();
for i in 0 .. splits.len() - 1 {
length += splits[i].len() + 1;
}
}
if let Some(rlength) = root_length {
if rlength > length {
root_length = Some(length);
}
} else {
root_length = Some(length);
}
}
archive = tarcrate::Archive::new(&buffer[..]);
}
#[cfg(feature = "indicate")]
let mut index = {
let number_of_files = archive.entries()?.collect::<Vec<_>>().len();
archive = tarcrate::Archive::new(&buffer[..]);
(0 , number_of_files)
};
for file in archive.entries()? {
let mut file = file?;
let mut unpack_path : Option<PathBuf> = None;
if file.header().path()?.to_str().unwrap().chars().last().unwrap() == "/".chars().last().unwrap() {
continue;
}
match file.header().path()?.to_str() {
None => { return Err(format_err!("Can't get file from archive")); }
Some(file_name) => {
if let Some(root_length) = root_length {
#[cfg(feature = "indicate")]
{
index.0 += 1;
bar.set_message(&format!("{} : {} / {}",
file_name[root_length..].to_string(),
index.0,
index.1
));
}
let mut new_file_path = des.clone();
new_file_path.push(file_name[root_length..].to_string());
if let Some(parent) = new_file_path.parent() {
fs::create_dir_all(parent)?;
}
unpack_path = Some(new_file_path);
}
}
}
if let Some(path) = unpack_path {
file.unpack(path)?;
}
}
#[cfg(feature = "indicate")]
bar.finish_with_message(&format!("Extract complete",));
Ok(des.clone())
}