···13131414[dependencies]
1515clap = { version = "4.2.1", features = ["derive"] }
1616+flate2 = "1.0"
1617tar = "0.4.38"
1718
+2-1
README.md
···4455A compression multi-tool for the CLI.
6677-Currently supports basic operations on `tar` archives.
77+Currently supports basic operations on `tar` and `gzip` archives.
8899## Supported formats
10101111+- gzip
1112- tar
+33
src/gzip.rs
···11+extern crate flate2;
22+33+use flate2::write::GzEncoder;
44+use flate2::{read::GzDecoder, Compression};
55+use std::fs::File;
66+use std::io::{Read, Write};
77+use std::path::Path;
88+99+/// The standard extension for the gzip format.
1010+pub const EXT: &str = "gz";
1111+1212+pub fn compress_file<I: AsRef<Path>, O: Write>(in_file: I, output: O, level: u32) {
1313+ compress(File::open(in_file).unwrap(), output, level);
1414+}
1515+1616+/// Compress an input file or directory into a gzip archive.
1717+pub fn compress<I: Read, O: Write>(mut input: I, output: O, level: u32) {
1818+ let mut encoder = GzEncoder::new(output, Compression::new(level));
1919+2020+ std::io::copy(&mut input, &mut encoder).unwrap();
2121+ encoder.finish().unwrap();
2222+}
2323+2424+/// Extract a gzipped file
2525+pub fn extract_file<I: AsRef<Path>, O: Write>(in_file: I, output: O) {
2626+ extract(File::open(in_file).unwrap(), output);
2727+}
2828+2929+/// Extract the gzip compressed data
3030+pub fn extract<I: Read, O: Write>(input: I, mut output: O) {
3131+ let mut decoder = GzDecoder::new(input);
3232+ std::io::copy(&mut decoder, &mut output).unwrap();
3333+}
+55-4
src/main.rs
···11+mod gzip;
12mod tar;
2334use clap::{Args, Parser, Subcommand};
···20212122 /// extract by guessing the format
2223 Extract(ExtractArgs),
2424+2525+ /// gzip compression
2626+ Gzip(GzipArgs),
2327}
24282529#[derive(Args, Debug)]
···4044 input: String,
41454246 /// Output file/directory
4343- ///
4444- /// If it's not provided, the extension is inferred from the compression type.
4547 #[arg(index = 2)]
4648 output: Option<String>,
4749···4951 #[arg(short, long)]
5052 compress: bool,
51535252- /// Extract the input file
5454+ /// Extract the input
5355 #[arg(short, long)]
5456 extract: bool,
5557}
56585959+#[derive(Args, Debug)]
6060+struct GzipArgs {
6161+ /// Input file
6262+ #[arg(index = 1)]
6363+ input: String,
6464+6565+ /// Output file/directory
6666+ #[arg(index = 2)]
6767+ output: Option<String>,
6868+6969+ /// Compress the input (default)
7070+ #[arg(short, long)]
7171+ compress: bool,
7272+7373+ /// Extract the input
7474+ #[arg(short, long)]
7575+ extract: bool,
7676+7777+ /// Level of compression
7878+ ///
7979+ /// This is an int 0-9, with 0 being no compression and 9 being highest compression.
8080+ #[arg(long, default_value_t = 6)]
8181+ compression: u32,
8282+}
8383+5784/// Generates the output filename.
5885/// This either takes the given name or guesses the name based on the extension
5986fn output_filename(input: &Path, output: Option<String>, extension: &str) -> String {
···82109 // Compress by default, warn if if looks like an archive.
83110 if input_path.extension().unwrap() == tar::EXT {
84111 println!(
8585- "error: input appears to be a tar archive, exiting. Use '--compress' if needed."
112112+ "error: input appears to already be a tar archive, exiting. Use '--compress' if needed."
86113 )
87114 } else {
88115 let out = output_filename(input_path, args.output, tar::EXT);
···91118 }
92119}
93120121121+/// Execute a gzip command
122122+fn command_gzip(args: GzipArgs) {
123123+ let input_path = Path::new(&args.input);
124124+ if args.compress {
125125+ let out = output_filename(input_path, args.output, gzip::EXT);
126126+ gzip::compress_file(input_path, File::create(out).unwrap(), args.compression);
127127+ } else if args.extract {
128128+ assert!(args.output.is_some(), "error: output filename required");
129129+ gzip::extract_file(input_path, File::create(args.output.unwrap()).unwrap());
130130+ } else {
131131+ // Neither is set.
132132+ // Compress by default, warn if if looks like an archive.
133133+ if input_path.extension().unwrap() == gzip::EXT {
134134+ println!(
135135+ "error: input appears to already be a gzip archive, exiting. Use '--compress' if needed."
136136+ )
137137+ } else {
138138+ let out = output_filename(input_path, args.output, gzip::EXT);
139139+ gzip::compress_file(input_path, File::create(out).unwrap(), args.compression);
140140+ }
141141+ }
142142+}
143143+94144/// Execute an extract command.
95145///
96146/// Attempts to extract based on the file extension.
···107157 match args.format {
108158 Some(Format::Tar(a)) => command_tar(a),
109159 Some(Format::Extract(a)) => command_extract(a),
160160+ Some(Format::Gzip(a)) => command_gzip(a),
110161 None => println!("none"),
111162 };
112163}