this repo has no description
1pub mod backends;
2mod job;
3pub mod progress;
4#[cfg(test)]
5pub mod test_utils;
6pub mod utils;
7
8use backends::*;
9use clap::{CommandFactory, Parser, Subcommand};
10use clap_complete::Shell;
11use job::{Action, get_job};
12use utils::*;
13
14/// A compression multi-tool
15#[derive(Parser, Debug)]
16#[command(author, version, about, long_about = None)]
17struct CmprssArgs {
18 /// Format
19 #[command(subcommand)]
20 format: Option<Format>,
21
22 // Base arguments for the non-subcommand behavior
23 #[clap(flatten)]
24 pub base_args: CommonArgs,
25}
26
27#[derive(Subcommand, Debug)]
28enum Format {
29 /// tar archive format
30 Tar(TarArgs),
31
32 /// gzip compression
33 #[clap(visible_alias = "gz")]
34 Gzip(GzipArgs),
35
36 /// xz compression
37 Xz(XzArgs),
38
39 /// bzip2 compression
40 #[clap(visible_alias = "bz2")]
41 Bzip2(Bzip2Args),
42
43 /// zip archive format
44 Zip(ZipArgs),
45
46 /// zstd compression
47 #[clap(visible_alias = "zst")]
48 Zstd(ZstdArgs),
49
50 /// lz4 compression
51 Lz4(Lz4Args),
52
53 /// brotli compression
54 #[clap(visible_alias = "br")]
55 Brotli(BrotliArgs),
56
57 /// snappy framed compression
58 #[clap(visible_alias = "sz")]
59 Snappy(SnappyArgs),
60
61 /// lzma (legacy LZMA1) compression
62 Lzma(LzmaArgs),
63
64 /// 7-Zip archive format
65 #[clap(name = "7z", visible_alias = "sevenz")]
66 SevenZ(SevenZArgs),
67
68 /// Print a shell completion script to stdout.
69 #[clap(hide = true)]
70 Completions {
71 /// Shell to generate completions for.
72 #[arg(value_enum)]
73 shell: Shell,
74 },
75
76 /// Print the cmprss(1) man page (in troff format) to stdout.
77 #[clap(hide = true)]
78 Manpage,
79}
80
81/// If the first positional arg looks like a dotted format string (e.g.
82/// `tar.gz`, `tgz`) and isn't an existing path on disk, remove it from
83/// `io_list` and return the equivalent compressor chain. This gives the
84/// compound formats the same ergonomic treatment as the `tar` subcommand
85/// without cluttering `--help` with every permutation.
86fn take_format_prefix(io_list: &mut Vec<String>) -> Option<Box<dyn Compressor>> {
87 let first = io_list.first()?;
88 if std::path::Path::new(first).exists() {
89 return None;
90 }
91 let chain = chain_from_format_str(first)?;
92 let format = first.clone();
93 io_list.remove(0);
94 Some(Box::new(Pipeline::with_format(chain, format)))
95}
96
97fn write_completions(shell: Shell) -> Result {
98 let mut cmd = CmprssArgs::command();
99 clap_complete::generate(shell, &mut cmd, "cmprss", &mut std::io::stdout());
100 Ok(())
101}
102
103fn write_manpage() -> Result {
104 let cmd = CmprssArgs::command();
105 clap_mangen::Man::new(cmd).render(&mut std::io::stdout())?;
106 Ok(())
107}
108
109fn command(compressor: Option<Box<dyn Compressor>>, args: &CommonArgs) -> Result {
110 let job = get_job(compressor, args)?;
111 match job.action {
112 Action::Compress => job.compressor.compress(job.input, job.output),
113 Action::Extract => job.compressor.extract(job.input, job.output),
114 Action::List => job.compressor.list(job.input),
115 Action::Append => job.compressor.append(job.input, job.output),
116 }
117}
118
119fn main() {
120 let args = CmprssArgs::parse();
121 match args.format {
122 Some(Format::Tar(a)) => command(Some(Box::new(Tar::new(&a))), &a.common_args),
123 Some(Format::Gzip(a)) => command(Some(Box::new(Gzip::new(&a))), &a.common_args),
124 Some(Format::Xz(a)) => command(Some(Box::new(Xz::new(&a))), &a.common_args),
125 Some(Format::Bzip2(a)) => command(Some(Box::new(Bzip2::new(&a))), &a.common_args),
126 Some(Format::Zip(a)) => command(Some(Box::new(Zip::new(&a))), &a.common_args),
127 Some(Format::Zstd(a)) => command(Some(Box::new(Zstd::new(&a))), &a.common_args),
128 Some(Format::Lz4(a)) => command(Some(Box::new(Lz4::new(&a))), &a.common_args),
129 Some(Format::Brotli(a)) => command(Some(Box::new(Brotli::new(&a))), &a.common_args),
130 Some(Format::Snappy(a)) => command(Some(Box::new(Snappy::new(&a))), &a.common_args),
131 Some(Format::Lzma(a)) => command(Some(Box::new(Lzma::new(&a))), &a.common_args),
132 Some(Format::SevenZ(a)) => command(Some(Box::new(SevenZ::new(&a))), &a.common_args),
133 Some(Format::Completions { shell }) => write_completions(shell),
134 Some(Format::Manpage) => write_manpage(),
135 None => {
136 let mut base_args = args.base_args;
137 let compressor = take_format_prefix(&mut base_args.io_list);
138 command(compressor, &base_args)
139 }
140 }
141 .unwrap_or_else(|e| {
142 eprintln!("ERROR(cmprss): {}", e);
143 std::process::exit(1);
144 });
145}