// Decompress a file
//
// File at input_file_path is opened for reading, example: "archive.tar.gz"
// formats contains each format necessary for decompression, example: [Gz, Tar] (in decompression order)
// output_dir it's where the file will be decompressed to
// file_name is only used when extracting single file formats, no archive formats like .tar or .zip
Decompress a file
File at input_file_path is opened for reading, example: "archive.tar.gz"
formats contains each format necessary for decompression, example: [Gz, Tar] (in decompression order)
output_dir it's where the file will be decompressed to
file_name is only used when extracting single file formats, no archive formats like .tar or .zip
Decompress a file File at input_file_path is opened for reading example : " archive . tar . gz " formats contains each format necessary for decompression example : [ Gz Tar ] ( in decompression order ) output_dir it ' s where the file will be decompressed to file_name is only used when extracting single file formats no archive formats like . tar or . zip
fn decompress_file(
    input_file_path: &Path,
    formats: Vec<Extension>,
    output_dir: Option<&Path>,
    file_name: &Path,
    question_policy: QuestionPolicy,
) -> crate::Result<()> {
    // TODO: improve error message
    let reader = fs::File::open(&input_file_path)?;
    // Output path is used by single file formats
    let output_path =
        if let Some(output_dir) = output_dir { output_dir.join(file_name) } else { file_name.to_path_buf() };
    // Output folder is used by archive file formats (zip and tar)
    let output_dir = output_dir.unwrap_or_else(|| Path::new("."));
    // Zip archives are special, because they require io::Seek, so it requires it's logic separated
    // from decoder chaining.
    //
    // This is the only case where we can read and unpack it directly, without having to do
    // in-memory decompression/copying first.
    //
    // Any other Zip decompression done can take up the whole RAM and freeze ouch.
    if formats.len() == 1 && *formats[0].compression_formats.as_slice() == [Zip] {
        utils::create_dir_if_non_existent(output_dir)?;
        let zip_archive = zip::ZipArchive::new(reader)?;
        let _files = crate::archive::zip::unpack_archive(zip_archive, output_dir, question_policy)?;
        info!("Successfully decompressed archive in {}.", nice_directory_display(output_dir));
        return Ok(());
    }
    // Will be used in decoder chaining
    let reader = BufReader::with_capacity(BUFFER_CAPACITY, reader);
    let mut reader: Box<dyn Read> = Box::new(reader);
    // Grab previous decoder and wrap it inside of a new one
    let chain_reader_decoder = |format: &CompressionFormat, decoder: Box<dyn Read>| -> crate::Result<Box<dyn Read>> {
        let decoder: Box<dyn Read> = match format {
            Gzip => Box::new(flate2::read::GzDecoder::new(decoder)),
            Bzip => Box::new(bzip2::read::BzDecoder::new(decoder)),
            Lzma => Box::new(xz2::read::XzDecoder::new(decoder)),
            Zstd => Box::new(zstd::stream::Decoder::new(decoder)?),
            _ => unreachable!(),
        };
        Ok(decoder)
    };
    for format in formats.iter().flat_map(Extension::iter).skip(1).collect::<Vec<_>>().iter().rev() {
        reader = chain_reader_decoder(format, reader)?;
    }
    utils::create_dir_if_non_existent(output_dir)?;
    let files_unpacked;
    match formats[0].compression_formats[0] {
        Gzip | Bzip | Lzma | Zstd => {
            reader = chain_reader_decoder(&formats[0].compression_formats[0], reader)?;
            let writer = utils::create_or_ask_overwrite(&output_path, question_policy)?;
            if writer.is_none() {
                // Means that the user doesn't want to overwrite
                return Ok(());
            }
            let mut writer = writer.unwrap();
            io::copy(&mut reader, &mut writer)?;
            files_unpacked = vec![output_path];
        }
        Tar => {
            files_unpacked = crate::archive::tar::unpack_archive(reader, output_dir, question_policy)?;
        }
        Zip => {
            eprintln!("Compressing first into .zip.");
            eprintln!("Warning: .zip archives with extra extensions have a downside.");
            eprintln!(
                "The only way is loading everything into the RAM while compressing, and then write everything down."
            );
            eprintln!("this means that by compressing .zip with extra compression formats, you can run out of RAM if the file is too large!");
            let mut vec = vec![];
            io::copy(&mut reader, &mut vec)?;
            let zip_archive = zip::ZipArchive::new(io::Cursor::new(vec))?;
            files_unpacked = crate::archive::zip::unpack_archive(zip_archive, output_dir, question_policy)?;
        }
    }
    info!("Successfully decompressed archive in {}.", nice_directory_display(output_dir));
    info!("Files unpacked: {}", files_unpacked.len());
    Ok(())
}
fn decompress_file ( input_file_path : & Path , formats : Vec < Extension > , output_dir : Option < & Path > , file_name : & Path , question_policy : QuestionPolicy , ) -> crate :: Result < ( ) > { // TODO: improve error message let reader = fs :: File :: open ( & input_file_path ) ? ; // Output path is used by single file formats let output_path = if let Some ( output_dir ) = output_dir { output_dir . join ( file_name ) } else { file_name . to_path_buf ( ) } ; // Output folder is used by archive file formats (zip and tar) let output_dir = output_dir . unwrap_or_else ( | | Path :: new ( " " ) ) ; // Zip archives are special, because they require io::Seek, so it requires it's logic separated // from decoder chaining. // // This is the only case where we can read and unpack it directly, without having to do // in-memory decompression/copying first. // // Any other Zip decompression done can take up the whole RAM and freeze ouch. if formats . len ( ) == 1 && * formats [ 0 ] . compression_formats . as_slice ( ) == [ Zip ] { utils :: create_dir_if_non_existent ( output_dir ) ? ; let zip_archive = zip :: ZipArchive :: new ( reader ) ? ; let _files = crate :: archive :: zip :: unpack_archive ( zip_archive , output_dir , question_policy ) ? ; info ! ( " " nice_directory_display ( output_dir ) ) ; return Ok ( ( ) ) ; } // Will be used in decoder chaining let reader = BufReader :: with_capacity ( BUFFER_CAPACITY , reader ) ; let mut reader : Box < dyn Read > = Box :: new ( reader ) ; // Grab previous decoder and wrap it inside of a new one let chain_reader_decoder = | format : & CompressionFormat , decoder : Box < dyn Read > | -> crate :: Result < Box < dyn Read > > { let decoder : Box < dyn Read > = match format { Gzip => Box :: new ( flate2 :: read :: GzDecoder :: new ( decoder ) ) , Bzip => Box :: new ( bzip2 :: read :: BzDecoder :: new ( decoder ) ) , Lzma => Box :: new ( xz2 :: read :: XzDecoder :: new ( decoder ) ) , Zstd => Box :: new ( zstd :: stream :: Decoder :: new ( decoder ) ? ) , _ => unreachable ! ( ) , } ; Ok ( decoder ) } ; for format in formats . iter ( ) . flat_map ( Extension :: iter ) . skip ( 1 ) . collect :: < Vec < _ > > ( ) . iter ( ) . rev ( ) { reader = chain_reader_decoder ( format , reader ) ? ; } utils :: create_dir_if_non_existent ( output_dir ) ? ; let files_unpacked ; match formats [ 0 ] . compression_formats [ 0 ] { Gzip | Bzip | Lzma | Zstd => { reader = chain_reader_decoder ( & formats [ 0 ] . compression_formats [ 0 ] , reader ) ? ; let writer = utils :: create_or_ask_overwrite ( & output_path , question_policy ) ? ; if writer . is_none ( ) { // Means that the user doesn't want to overwrite return Ok ( ( ) ) ; } let mut writer = writer . unwrap ( ) ; io :: copy ( & mut reader , & mut writer ) ? ; files_unpacked = vec ! [ output_path ] ; } Tar => { files_unpacked = crate :: archive :: tar :: unpack_archive ( reader , output_dir , question_policy ) ? ; } Zip => { eprintln ! ( " " ) ; eprintln ! ( " " ) ; eprintln ! ( " " ) ; eprintln ! ( " " ) ; let mut vec = vec ! [ ] ; io :: copy ( & mut reader , & mut vec ) ? ; let zip_archive = zip :: ZipArchive :: new ( io :: Cursor :: new ( vec ) ) ? ; files_unpacked = crate :: archive :: zip :: unpack_archive ( zip_archive , output_dir , question_policy ) ? ; } } info ! ( " " nice_directory_display ( output_dir ) ) ; info ! ( " " files_unpacked len ( ) ) ; Ok ( ( ) ) }
Decompress a file
File at input_file_path is opened for reading, example: "archive.tar.gz"
formats contains each format necessary for decompression, example: [Gz, Tar] (in decompression order)
output_dir it's where the file will be decompressed to
file_name is only used when extracting single file formats, no archive formats like .tar or .zip
Decompress a file File at input_file_path is opened for reading example : " archive . tar . gz " formats contains each format necessary for decompression example : [ Gz Tar ] ( in decompression order ) output_dir it ' s where the file will be decompressed to file_name is only used when extracting single file formats no archive formats like . tar or . zip
input_file_path
&Path
formats
Vec<Extension>
output_dir
Option<&Path>
file_name
&Path
question_policy
QuestionPolicy