10 cli::{VERSION, Command},
12 Entry, Compressor, BzipCompressor, GzipCompressor, LzmaCompressor, TarCompressor,
16 BzipDecompressor, DecompressionResult, Decompressor, GzipDecompressor, LzmaDecompressor,
17 TarDecompressor, ZipDecompressor,
19 dialogs::Confirmation,
20 extension::{CompressionFormat, Extension},
25 pub struct Evaluator {}
27 type BoxedCompressor = Box<dyn Compressor>;
28 type BoxedDecompressor = Box<dyn Decompressor>;
31 pub fn get_compressor(
33 ) -> crate::Result<(Option<BoxedCompressor>, BoxedCompressor)> {
35 let extension = match &file.extension {
36 Some(extension) => extension.clone(),
38 // This block *should* be unreachable
40 "{} reached Evaluator::get_decompressor without known extension.",
41 "[internal error]".red()
43 return Err(crate::Error::InternalError);
47 // Supported first compressors:
49 let first_compressor: Option<Box<dyn Compressor>> = match extension.first_ext {
50 Some(ext) => match ext {
51 CompressionFormat::Tar => Some(Box::new(TarCompressor {})),
52 CompressionFormat::Zip => Some(Box::new(ZipCompressor {})),
53 // _other => Some(Box::new(NifflerCompressor {})),
61 // Supported second compressors:
63 let second_compressor: Box<dyn Compressor> = match extension.second_ext {
64 CompressionFormat::Tar => Box::new(TarCompressor {}),
65 CompressionFormat::Zip => Box::new(ZipCompressor {}),
66 CompressionFormat::Bzip => Box::new(BzipCompressor {}),
67 CompressionFormat::Gzip => Box::new(GzipCompressor {}),
68 CompressionFormat::Lzma => Box::new(LzmaCompressor {}),
71 Ok((first_compressor, second_compressor))
74 pub fn get_decompressor(
76 ) -> crate::Result<(Option<BoxedDecompressor>, BoxedDecompressor)> {
77 let extension = match &file.extension {
78 Some(extension) => extension.clone(),
80 // This block *should* be unreachable
82 "{} reached Evaluator::get_decompressor without known extension.",
83 "[internal error]".red()
85 return Err(crate::Error::InvalidInput);
89 let second_decompressor: Box<dyn Decompressor> = match extension.second_ext {
90 CompressionFormat::Tar => Box::new(TarDecompressor {}),
91 CompressionFormat::Zip => Box::new(ZipDecompressor {}),
92 CompressionFormat::Gzip => Box::new(GzipDecompressor {}),
93 CompressionFormat::Lzma => Box::new(LzmaDecompressor {}),
94 CompressionFormat::Bzip => Box::new(BzipDecompressor {}),
97 let first_decompressor: Option<Box<dyn Decompressor>> = match extension.first_ext {
98 Some(ext) => match ext {
99 CompressionFormat::Tar => Some(Box::new(TarDecompressor {})),
100 CompressionFormat::Zip => Some(Box::new(ZipDecompressor {})),
106 Ok((first_decompressor, second_decompressor))
109 fn decompress_file_in_memory(
112 decompressor: Option<Box<dyn Decompressor>>,
113 output_file: Option<File>,
114 extension: Option<Extension>,
116 ) -> crate::Result<()> {
117 let output_file_path = utils::get_destination_path(&output_file);
119 let file_name = file_path
122 .unwrap_or(output_file_path);
124 if "." == file_name.as_os_str() {
125 // I believe this is only possible when the supplied input has a name
126 // of the sort `.tar` or `.zip' and no output has been supplied.
127 // file_name = OsStr::new("ouch-output");
128 todo!("Pending review, what is this supposed to do??");
131 // If there is a decompressor to use, we'll create a file in-memory and decompress it
132 let decompressor = match decompressor {
133 Some(decompressor) => decompressor,
135 // There is no more processing to be done on the input file (or there is but currently unsupported)
136 // Therefore, we'll save what we have in memory into a file.
137 println!("{}: saving to {:?}.", "info".yellow(), file_name);
139 if file_name.exists() {
141 Confirmation::new("Do you want to overwrite 'FILE'?", Some("FILE"));
142 if !utils::permission_for_overwriting(&file_name, flags, &confirm)? {
147 let mut f = fs::File::create(output_file_path.join(file_name))?;
148 f.write_all(&bytes)?;
155 contents_in_memory: Some(bytes),
159 let decompression_result = decompressor.decompress(file, &output_file, flags)?;
160 if let DecompressionResult::FileInMemory(_) = decompression_result {
161 unreachable!("Shouldn't");
171 ) -> crate::Result<()> {
172 let mut output = File::from(output_path)?;
174 let confirm = Confirmation::new("Do you want to overwrite 'FILE'?", Some("FILE"));
175 let (first_compressor, second_compressor) = Self::get_compressor(&output)?;
177 // TODO: use -y and -n here
178 if output_path.exists()
179 && !utils::permission_for_overwriting(&output_path, flags, &confirm)?
181 // The user does not want to overwrite the file
185 let bytes = match first_compressor {
186 Some(first_compressor) => {
187 let mut entry = Entry::Files(files);
188 let bytes = first_compressor.compress(entry)?;
190 output.contents_in_memory = Some(bytes);
191 entry = Entry::InMemory(output);
192 second_compressor.compress(entry)?
195 let entry = Entry::Files(files);
196 second_compressor.compress(entry)?
201 "{}: writing to {:?}. ({} bytes)",
206 fs::write(output_path, bytes)?;
213 output: Option<&Path>,
215 ) -> crate::Result<()> {
216 let file = File::from(file_path)?;
217 let output = match output {
218 Some(inner) => Some(File::from(inner)?),
221 let (first_decompressor, second_decompressor) = Self::get_decompressor(&file)?;
223 let extension = file.extension.clone();
225 let decompression_result = second_decompressor.decompress(file, &output, &flags)?;
227 match decompression_result {
228 DecompressionResult::FileInMemory(bytes) => {
229 // We'll now decompress a file currently in memory.
230 // This will currently happen in the case of .bz, .xz and .lzma
231 Self::decompress_file_in_memory(
240 DecompressionResult::FilesUnpacked(_files) => {
241 // If the file's last extension was an archival method,
242 // such as .tar, .zip or (to-do) .rar, then we won't look for
243 // further processing.
244 // The reason for this is that cases such as "file.xz.tar" are too rare
245 // to worry about, at least at the moment.
247 // TODO: use the `files` variable for something
254 pub fn evaluate(command: Command, flags: &oof::Flags) -> crate::Result<()> {
258 compressed_output_path,
259 } => Self::compress_files(files, &compressed_output_path, flags)?,
260 Command::Decompress {
264 // From Option<PathBuf> to Option<&Path>
265 let output_folder = output_folder.as_ref().map(|path| Path::new(path));
266 for file in files.iter() {
267 Self::decompress_file(file, output_folder, flags)?;
270 Command::ShowHelp => help_message(),
271 Command::ShowVersion => version_message(),
278 fn version_message() {
279 println!("ouch {}", VERSION);
284 println!("Vinícius R. M. & João M. Bezerra");
285 println!("ouch is a unified compression & decompression utility");
287 println!(" COMPRESSION USAGE:");
288 println!(" ouch compress <input...> output-file");
289 println!("DECOMPRESSION USAGE:");
290 println!(" ouch <input> [-o/--output output-folder]");