···11mod tar;
2233-use clap::Parser;
33+use clap::{Args, Parser, Subcommand};
44use std::path::Path;
5566/// A compression multi-tool
77#[derive(Parser, Debug)]
88#[command(author, version, about, long_about = None)]
99-struct Args {
99+struct CmprssArgs {
1010+ /// Format
1111+ #[command(subcommand)]
1212+ format: Option<Format>,
1313+}
1414+1515+#[derive(Subcommand, Debug)]
1616+enum Format {
1717+ /// tar archive format
1818+ Tar(TarArgs),
1919+2020+ /// extract by guessing the format
2121+ Extract(ExtractArgs),
2222+}
2323+2424+#[derive(Args, Debug)]
2525+struct ExtractArgs {
2626+ /// Input file
2727+ #[arg(index = 1)]
2828+ input: String,
2929+3030+ /// Output file/directory
3131+ #[arg(index = 2)]
3232+ output: Option<String>,
3333+}
3434+3535+#[derive(Args, Debug)]
3636+struct TarArgs {
1037 /// Input file
1138 #[arg(index = 1)]
1239 input: String,
···1744 #[arg(index = 2)]
1845 output: Option<String>,
19462020- /// Compress the input
4747+ /// Compress the input (default)
2148 #[arg(short, long)]
2249 compress: bool,
23502424- /// Extract the input
5151+ /// Extract the input file
2552 #[arg(short, long)]
2653 extract: bool,
2754}
···4168 }
4269}
43704444-fn main() {
4545- let args = Args::parse();
7171+/// Execute a tar command
7272+fn command_tar(args: TarArgs) {
4673 let input_path = Path::new(&args.input);
4774 if args.compress {
4848- let out = output_filename(input_path, args.output, tar::extension());
7575+ let out = output_filename(input_path, args.output, tar::EXT);
4976 tar::compress(input_path, out);
5077 } else if args.extract {
5178 tar::extract(input_path, args.output.unwrap_or(".".to_string()));
5279 } else {
5353- // Neither set, so infer based on filename
5454- if input_path.extension().unwrap() == tar::extension() {
5555- tar::extract(input_path, args.output.unwrap_or(".".to_string()));
8080+ // Neither is set.
8181+ // Compress by default, warn if if looks like an archive.
8282+ if input_path.extension().unwrap() == tar::EXT {
8383+ println!(
8484+ "error: input appears to be a tar archive, exiting. Use '--compress' if needed."
8585+ )
5686 } else {
5757- let out = output_filename(input_path, args.output, tar::extension());
8787+ let out = output_filename(input_path, args.output, tar::EXT);
5888 tar::compress(input_path, out);
5989 }
6090 }
6191}
9292+9393+/// Execute an extract command.
9494+///
9595+/// Attempts to extract based on the file extension.
9696+fn command_extract(args: ExtractArgs) {
9797+ let input_path = Path::new(&args.input);
9898+ match input_path.extension().unwrap().to_str().unwrap() {
9999+ tar::EXT => tar::extract(input_path, args.output.unwrap_or(".".to_string())),
100100+ _ => println!("error: unknown format "),
101101+ }
102102+}
103103+104104+fn main() {
105105+ let args = CmprssArgs::parse();
106106+ match args.format {
107107+ Some(Format::Tar(a)) => command_tar(a),
108108+ Some(Format::Extract(a)) => command_extract(a),
109109+ None => println!("none"),
110110+ };
111111+}
+2-4
src/tar.rs
···44use std::path::Path;
55use tar::{Archive, Builder};
6677-/// Return the standard extension for the tar format.
88-pub fn extension() -> &'static str {
99- "tar"
1010-}
77+/// The standard extension for the tar format.
88+pub const EXT: &str = "tar";
1191210/// Compress an input file or directory into a tar archive.
1311pub fn compress<I: AsRef<Path>, O: AsRef<Path>>(in_file: I, out_file: O) {