refactor: improve code formatting in `mod.rs` and `logger.rs`
[ouch.git] / src / commands / list.rs
blob1821d6da49d82e635d80b0c45476e6adb6631c6e
1 use std::{
2     io::{self, BufReader, Read},
3     path::Path,
4 };
6 use fs_err as fs;
8 use crate::{
9     archive::sevenz,
10     commands::warn_user_about_loading_zip_in_memory,
11     extension::CompressionFormat::{self, *},
12     list::{self, FileInArchive, ListOptions},
13     utils::{io::lock_and_flush_output_stdio, user_wants_to_continue},
14     QuestionAction, QuestionPolicy, BUFFER_CAPACITY,
17 /// File at input_file_path is opened for reading, example: "archive.tar.gz"
18 /// formats contains each format necessary for decompression, example: [Gz, Tar] (in decompression order)
19 pub fn list_archive_contents(
20     archive_path: &Path,
21     formats: Vec<CompressionFormat>,
22     list_options: ListOptions,
23     question_policy: QuestionPolicy,
24     password: Option<&[u8]>,
25 ) -> crate::Result<()> {
26     let reader = fs::File::open(archive_path)?;
28     // Zip archives are special, because they require io::Seek, so it requires it's logic separated
29     // from decoder chaining.
30     //
31     // This is the only case where we can read and unpack it directly, without having to do
32     // in-memory decompression/copying first.
33     //
34     // Any other Zip decompression done can take up the whole RAM and freeze ouch.
35     if let &[Zip] = formats.as_slice() {
36         let zip_archive = zip::ZipArchive::new(reader)?;
37         let files = crate::archive::zip::list_archive(zip_archive, password);
38         list::list_files(archive_path, files, list_options)?;
40         return Ok(());
41     }
43     // Will be used in decoder chaining
44     let reader = BufReader::with_capacity(BUFFER_CAPACITY, reader);
45     let mut reader: Box<dyn Read + Send> = Box::new(reader);
47     // Grab previous decoder and wrap it inside of a new one
48     let chain_reader_decoder =
49         |format: &CompressionFormat, decoder: Box<dyn Read + Send>| -> crate::Result<Box<dyn Read + Send>> {
50             let decoder: Box<dyn Read + Send> = match format {
51                 Gzip => Box::new(flate2::read::GzDecoder::new(decoder)),
52                 Bzip => Box::new(bzip2::read::BzDecoder::new(decoder)),
53                 Bzip3 => Box::new(bzip3::read::Bz3Decoder::new(decoder).unwrap()),
54                 Lz4 => Box::new(lz4_flex::frame::FrameDecoder::new(decoder)),
55                 Lzma => Box::new(xz2::read::XzDecoder::new(decoder)),
56                 Snappy => Box::new(snap::read::FrameDecoder::new(decoder)),
57                 Zstd => Box::new(zstd::stream::Decoder::new(decoder)?),
58                 Tar | Zip | Rar | SevenZip => unreachable!(),
59             };
60             Ok(decoder)
61         };
63     for format in formats.iter().skip(1).rev() {
64         reader = chain_reader_decoder(format, reader)?;
65     }
67     let files: Box<dyn Iterator<Item = crate::Result<FileInArchive>>> = match formats[0] {
68         Tar => Box::new(crate::archive::tar::list_archive(tar::Archive::new(reader))),
69         Zip => {
70             if formats.len() > 1 {
71                 // Locking necessary to guarantee that warning and question
72                 // messages stay adjacent
73                 let _locks = lock_and_flush_output_stdio();
75                 warn_user_about_loading_zip_in_memory();
76                 if !user_wants_to_continue(archive_path, question_policy, QuestionAction::Decompression)? {
77                     return Ok(());
78                 }
79             }
81             let mut vec = vec![];
82             io::copy(&mut reader, &mut vec)?;
83             let zip_archive = zip::ZipArchive::new(io::Cursor::new(vec))?;
85             Box::new(crate::archive::zip::list_archive(zip_archive, password))
86         }
87         #[cfg(feature = "unrar")]
88         Rar => {
89             if formats.len() > 1 {
90                 let mut temp_file = tempfile::NamedTempFile::new()?;
91                 io::copy(&mut reader, &mut temp_file)?;
92                 Box::new(crate::archive::rar::list_archive(temp_file.path(), password)?)
93             } else {
94                 Box::new(crate::archive::rar::list_archive(archive_path, password)?)
95             }
96         }
97         #[cfg(not(feature = "unrar"))]
98         Rar => {
99             return Err(crate::archive::rar_stub::no_support());
100         }
101         SevenZip => {
102             if formats.len() > 1 {
103                 // Locking necessary to guarantee that warning and question
104                 // messages stay adjacent
105                 let _locks = lock_and_flush_output_stdio();
107                 warn_user_about_loading_zip_in_memory();
108                 if !user_wants_to_continue(archive_path, question_policy, QuestionAction::Decompression)? {
109                     return Ok(());
110                 }
111             }
113             Box::new(sevenz::list_archive(archive_path, password)?)
114         }
115         Gzip | Bzip | Bzip3 | Lz4 | Lzma | Snappy | Zstd => {
116             panic!("Not an archive! This should never happen, if it does, something is wrong with `CompressionFormat::is_archive()`. Please report this error!");
117         }
118     };
120     list::list_files(archive_path, files, list_options)