tests: Add tests for extension extraction
[ouch.git] / src / cli.rs
blobf8d5c3e2a413ec0595ad1f62a0a850c80ae90806
1 use std::{convert::TryFrom, path::PathBuf, vec::Vec};
3 use clap::{Arg, Values};
4 use colored::Colorize;
6 use crate::error;
7 use crate::extensions::CompressionExtension;
8 use crate::file::File;
10 #[derive(PartialEq, Eq, Debug)]
11 pub enum CommandType {
12     Compression(
13         // Files to be compressed
14         Vec<PathBuf>,
15     ),
16     Decompression(
17         // Files to be decompressed and their extensions
18         Vec<(PathBuf, CompressionExtension)>,
19     ),
22 #[derive(PartialEq, Eq, Debug)]
23 pub struct Command {
24     pub command_type: CommandType,
25     pub output: Option<File>,
28 pub fn clap_app<'a, 'b>() -> clap::App<'a, 'b> {
29     clap::App::new("ouch")
30         .version("0.1.0")
31         .about("ouch is a unified compression & decompression utility")
32         .help_message("Displays this message and exits")
33         .settings(&[
34             clap::AppSettings::ColoredHelp,
35             clap::AppSettings::ArgRequiredElseHelp,
36         ])
37         .arg(
38             Arg::with_name("input")
39                 .required(true)
40                 .multiple(true)
41                 .long("input")
42                 .short("i")
43                 .help("Input files (TODO description)")
44                 .takes_value(true),
45         )
46         .arg(
47             Arg::with_name("output")
48                 // --output/-o not required when output can be inferred from the input files
49                 .required(false)
50                 .multiple(false)
51                 .long("output")
52                 .short("o")
53                 .help("Output file (TODO description)")
54                 .takes_value(true),
55         )
58 pub fn get_matches() -> clap::ArgMatches<'static> {
59     clap_app().get_matches()
62 // holy spaghetti code
63 impl TryFrom<clap::ArgMatches<'static>> for Command {
64     type Error = error::Error;
66     fn try_from(matches: clap::ArgMatches<'static>) -> error::OuchResult<Command> {
67         let process_decompressible_input = |input_files: Values| {
68             let input_files =
69                 input_files.map(|filename| (filename, CompressionExtension::try_from(filename)));
71             for file in input_files.clone() {
72                 if let (file, Err(_)) = file {
73                     // eprintln!("{}: file '{}' is not decompressible.", "error".red(), file);
74                     return Err(error::Error::InputsMustHaveBeenDecompressible(file.into()));
75                 }
76             }
78             Ok(input_files
79                 .map(|(filename, extension)| (PathBuf::from(filename), extension.unwrap()))
80                 .collect::<Vec<_>>())
81         };
83         // Possibilities:
84         //   * Case 1: output not supplied, therefore try to infer output by checking if all input files are decompressible
85         //   * Case 2: output supplied
87         let output_was_supplied = matches.is_present("output");
89         let input_files = matches.values_of("input").unwrap(); // Safe to unwrap since input is an obligatory argument
91         if output_was_supplied {
92             let output_file = matches.value_of("output").unwrap(); // Safe unwrap since we've established that output was supplied
94             let output_file_extension = CompressionExtension::try_from(output_file);
95             let output_is_compressible = output_file_extension.is_ok();
96             if output_is_compressible {
97                 println!(
98                     "{}: trying to compress input files into '{}'",
99                     "info".yellow(),
100                     output_file
101                 );
103                 let input_files = input_files.map(PathBuf::from).collect();
105                 return Ok(Command {
106                     command_type: CommandType::Compression(input_files),
107                     output: Some(File::WithExtension((
108                         output_file.into(),
109                         output_file_extension.unwrap(),
110                     ))),
111                 });
112             } else {
113                 // Checking if input files are decompressible
115                 let input_files = process_decompressible_input(input_files)?;
117                 println!(
118                     "{}: attempting to decompress input files into {}",
119                     "info".yellow(),
120                     output_file
121                 );
122                 return Ok(Command {
123                     command_type: CommandType::Decompression(input_files),
124                     output: Some(File::WithoutExtension(output_file.into())),
125                 });
126             }
127         } else {
128             // else: output file not supplied
129             // Case 1: all input files are decompressible
130             // Case 2: error
131             let input_files = process_decompressible_input(input_files)?;
132             return Ok(Command {
133                 command_type: CommandType::Decompression(input_files),
134                 output: None,
135             });
136         }
137     }