this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor: Move the args and parsing logic into compressor modules

+187 -167
+20
src/bzip2.rs
··· 1 1 use crate::utils::*; 2 2 use bzip2::write::{BzDecoder, BzEncoder}; 3 3 use bzip2::Compression; 4 + use clap::Args; 4 5 use std::{ 5 6 fs::File, 6 7 io::{self, Read, Write}, 7 8 }; 8 9 10 + #[derive(Args, Debug)] 11 + pub struct Bzip2Args { 12 + #[clap(flatten)] 13 + pub common_args: CommonArgs, 14 + 15 + /// Level of compression. 16 + /// This is an int 0-9, with 0 being no compression and 9 being highest compression. 17 + #[arg(long, default_value_t = 6)] 18 + level: u32, 19 + // TODO: Support keywords none, fast, best 20 + // Correspond to 0, 1, 9 21 + } 22 + 9 23 pub struct Bzip2 { 10 24 pub level: u32, // 0-9 11 25 } ··· 13 27 impl Default for Bzip2 { 14 28 fn default() -> Self { 15 29 Bzip2 { level: 6 } 30 + } 31 + } 32 + 33 + impl Bzip2 { 34 + pub fn new(args: &Bzip2Args) -> Self { 35 + Bzip2 { level: args.level } 16 36 } 17 37 } 18 38
+20
src/gzip.rs
··· 1 1 use crate::utils::*; 2 + use clap::Args; 2 3 use flate2::write::GzEncoder; 3 4 use flate2::{read::GzDecoder, Compression}; 4 5 use std::fs::File; 5 6 use std::io::{self, Read, Write}; 6 7 8 + #[derive(Args, Debug)] 9 + pub struct GzipArgs { 10 + #[clap(flatten)] 11 + pub common_args: CommonArgs, 12 + 13 + /// Level of compression. 14 + /// This is an int 0-9, with 0 being no compression and 9 being highest compression. 15 + #[arg(long, default_value_t = 6)] 16 + compression: u32, 17 + } 18 + 7 19 pub struct Gzip { 8 20 pub compression_level: u32, 9 21 } ··· 12 24 fn default() -> Self { 13 25 Gzip { 14 26 compression_level: 6, 27 + } 28 + } 29 + } 30 + 31 + impl Gzip { 32 + pub fn new(args: &GzipArgs) -> Gzip { 33 + Gzip { 34 + compression_level: args.compression, 15 35 } 16 36 } 17 37 }
+9 -157
src/main.rs
··· 5 5 mod utils; 6 6 mod xz; 7 7 8 - use clap::{Args, Parser, Subcommand}; 8 + use bzip2::{Bzip2, Bzip2Args}; 9 + use clap::{Parser, Subcommand}; 10 + use gzip::{Gzip, GzipArgs}; 9 11 use is_terminal::IsTerminal; 10 - use progress::ProgressDisplay; 11 12 use std::io; 12 13 use std::path::{Path, PathBuf}; 13 - use std::str::FromStr; 14 + use tar::{Tar, TarArgs}; 14 15 use utils::*; 16 + use xz::{Xz, XzArgs}; 15 17 16 18 /// A compression multi-tool 17 19 #[derive(Parser, Debug)] ··· 39 41 Bzip2(Bzip2Args), 40 42 } 41 43 42 - #[derive(Args, Debug)] 43 - struct TarArgs { 44 - #[clap(flatten)] 45 - common_args: CommonArgs, 46 - } 47 - 48 - #[derive(Debug, Clone)] 49 - struct ChunkSize { 50 - size_in_bytes: usize, 51 - } 52 - 53 - impl FromStr for ChunkSize { 54 - type Err = &'static str; 55 - 56 - fn from_str(s: &str) -> Result<Self, Self::Err> { 57 - // Try to parse s as just a number 58 - if let Ok(num) = s.parse::<usize>() { 59 - return Ok(ChunkSize { size_in_bytes: num }); 60 - } 61 - // Simplify so that we always assume base 2, regardless of whether we see 62 - // 'kb' or 'kib' 63 - let mut s = s.to_lowercase(); 64 - if s.ends_with("ib") { 65 - s.truncate(s.len() - 2); 66 - s.push('b'); 67 - }; 68 - let (num_str, unit) = s.split_at(s.len() - 2); 69 - let num = num_str.parse::<usize>().map_err(|_| "Invalid number")?; 70 - 71 - let size_in_bytes = match unit { 72 - "kb" => num * 1024, 73 - "mb" => num * 1024 * 1024, 74 - "gb" => num * 1024 * 1024 * 1024, 75 - _ => return Err("Invalid unit"), 76 - }; 77 - 78 - Ok(ChunkSize { size_in_bytes }) 79 - } 80 - } 81 - 82 - #[derive(Args, Debug)] 83 - struct ProgressArgs { 84 - /// Show progress. 85 - #[arg(long, value_enum, default_value = "auto")] 86 - progress: ProgressDisplay, 87 - 88 - /// Chunk size to use during the copy when showing the progress bar. 89 - #[arg(long, default_value = "8kib")] 90 - chunk_size: ChunkSize, 91 - } 92 - 93 - #[derive(Args, Debug)] 94 - struct CommonArgs { 95 - /// Input file/directory 96 - #[arg(short, long)] 97 - input: Option<String>, 98 - 99 - /// Output file/directory 100 - #[arg(short, long)] 101 - output: Option<String>, 102 - 103 - /// Compress the input (default) 104 - #[arg(short, long)] 105 - compress: bool, 106 - 107 - /// Extract the input 108 - #[arg(short, long)] 109 - extract: bool, 110 - 111 - /// List of I/O 112 - /// This consists of all the inputs followed by the single output, with intelligent fallback to stdin/stdout. 113 - #[arg()] 114 - io_list: Vec<String>, 115 - 116 - /// Ignore pipes when inferring I/O 117 - #[arg(long)] 118 - ignore_pipes: bool, 119 - 120 - /// Ignore stdin when inferring I/O 121 - #[arg(long)] 122 - ignore_stdin: bool, 123 - 124 - /// Ignore stdout when inferring I/O 125 - #[arg(long)] 126 - ignore_stdout: bool, 127 - } 128 - 129 - #[derive(Args, Debug)] 130 - struct GzipArgs { 131 - #[clap(flatten)] 132 - common_args: CommonArgs, 133 - 134 - /// Level of compression 135 - /// 136 - /// This is an int 0-9, with 0 being no compression and 9 being highest compression. 137 - #[arg(long, default_value_t = 6)] 138 - compression: u32, 139 - } 140 - 141 - #[derive(Args, Debug)] 142 - struct XzArgs { 143 - #[clap(flatten)] 144 - common_args: CommonArgs, 145 - 146 - #[clap(flatten)] 147 - progress_args: ProgressArgs, 148 - 149 - /// Level of compression 150 - /// 151 - /// This is an int 0-9, with 0 being no compression and 9 being highest compression. 152 - #[arg(long, default_value_t = 6)] 153 - level: u32, 154 - } 155 - 156 - #[derive(Args, Debug)] 157 - struct Bzip2Args { 158 - #[clap(flatten)] 159 - common_args: CommonArgs, 160 - 161 - /// Level of compression 162 - /// 163 - /// This is an int 0-9, with 0 being no compression and 9 being highest compression. 164 - /// TODO: Support keywords none, fast, best 165 - /// Correspond to 0, 1, 9 166 - #[arg(long, default_value_t = 6)] 167 - level: u32, 168 - } 169 - 170 44 /// Get the input filename or return a default file 171 45 /// This file will be used to generate the output filename 172 46 fn get_input_filename(input: &CmprssInput) -> Result<&Path, io::Error> { ··· 335 209 Ok(()) 336 210 } 337 211 338 - fn parse_gzip(args: &GzipArgs) -> gzip::Gzip { 339 - gzip::Gzip { 340 - compression_level: args.compression, 341 - } 342 - } 343 - 344 - fn parse_xz(args: &XzArgs) -> xz::Xz { 345 - xz::Xz { 346 - level: args.level, 347 - progress: args.progress_args.progress, 348 - chunk_size: args.progress_args.chunk_size.size_in_bytes, 349 - } 350 - } 351 - 352 - fn parse_bzip2(args: &Bzip2Args) -> bzip2::Bzip2 { 353 - bzip2::Bzip2 { level: args.level } 354 - } 355 - 356 - fn parse_tar(_args: &TarArgs) -> tar::Tar { 357 - tar::Tar {} 358 - } 359 - 360 212 fn main() { 361 213 let args = CmprssArgs::parse(); 362 214 match args.format { 363 - Some(Format::Tar(a)) => command(parse_tar(&a), &a.common_args), 364 - Some(Format::Gzip(a)) => command(parse_gzip(&a), &a.common_args), 365 - Some(Format::Xz(a)) => command(parse_xz(&a), &a.common_args), 366 - Some(Format::Bzip2(a)) => command(parse_bzip2(&a), &a.common_args), 215 + Some(Format::Tar(a)) => command(Tar::new(&a), &a.common_args), 216 + Some(Format::Gzip(a)) => command(Gzip::new(&a), &a.common_args), 217 + Some(Format::Xz(a)) => command(Xz::new(&a), &a.common_args), 218 + Some(Format::Bzip2(a)) => command(Bzip2::new(&a), &a.common_args), 367 219 _ => Err(io::Error::new(io::ErrorKind::Other, "unknown input")), 368 220 } 369 221 .unwrap_or_else(|e| {
+57 -1
src/progress.rs
··· 1 1 use crate::utils::CmprssOutput; 2 + use clap::Args; 2 3 use indicatif::{HumanBytes, ProgressBar}; 4 + use std::str::FromStr; 3 5 4 - #[derive(clap::ValueEnum, Clone, Copy, Debug)] 6 + #[derive(clap::ValueEnum, Clone, Copy, Debug, Default)] 5 7 pub enum ProgressDisplay { 8 + #[default] 6 9 Auto, 7 10 On, 8 11 Off, 12 + } 13 + 14 + #[derive(Debug, Clone, Copy)] 15 + pub struct ChunkSize { 16 + pub size_in_bytes: usize, 17 + } 18 + 19 + impl Default for ChunkSize { 20 + fn default() -> Self { 21 + ChunkSize { 22 + size_in_bytes: 8192, 23 + } 24 + } 25 + } 26 + 27 + impl FromStr for ChunkSize { 28 + type Err = &'static str; 29 + 30 + fn from_str(s: &str) -> Result<Self, Self::Err> { 31 + // Try to parse s as just a number 32 + if let Ok(num) = s.parse::<usize>() { 33 + return Ok(ChunkSize { size_in_bytes: num }); 34 + } 35 + // Simplify so that we always assume base 2, regardless of whether we see 36 + // 'kb' or 'kib' 37 + let mut s = s.to_lowercase(); 38 + if s.ends_with("ib") { 39 + s.truncate(s.len() - 2); 40 + s.push('b'); 41 + }; 42 + let (num_str, unit) = s.split_at(s.len() - 2); 43 + let num = num_str.parse::<usize>().map_err(|_| "Invalid number")?; 44 + 45 + let size_in_bytes = match unit { 46 + "kb" => num * 1024, 47 + "mb" => num * 1024 * 1024, 48 + "gb" => num * 1024 * 1024 * 1024, 49 + _ => return Err("Invalid unit"), 50 + }; 51 + 52 + Ok(ChunkSize { size_in_bytes }) 53 + } 54 + } 55 + 56 + #[derive(Args, Debug, Default, Clone, Copy)] 57 + pub struct ProgressArgs { 58 + /// Show progress. 59 + #[arg(long, value_enum, default_value = "auto")] 60 + pub progress: ProgressDisplay, 61 + 62 + /// Chunk size to use during the copy when showing the progress bar. 63 + #[arg(long, default_value = "8kib")] 64 + pub chunk_size: ChunkSize, 9 65 } 10 66 11 67 /// Progress bar for the compress process
+13
src/tar.rs
··· 1 1 extern crate tar; 2 2 3 + use clap::Args; 3 4 use std::fs::File; 4 5 use std::io::{self, Read, Write}; 5 6 use std::path::Path; 6 7 use tar::{Archive, Builder}; 7 8 8 9 use crate::utils::*; 10 + 11 + #[derive(Args, Debug)] 12 + pub struct TarArgs { 13 + #[clap(flatten)] 14 + pub common_args: CommonArgs, 15 + } 9 16 10 17 #[derive(Default)] 11 18 pub struct Tar {} 19 + 20 + impl Tar { 21 + pub fn new(_args: &TarArgs) -> Tar { 22 + Tar {} 23 + } 24 + } 12 25 13 26 impl Compressor for Tar { 14 27 /// Full name for tar, also used for extension
+37
src/utils.rs
··· 1 + use clap::Args; 1 2 use std::io; 2 3 use std::path::{Path, PathBuf}; 4 + 5 + #[derive(Args, Debug)] 6 + pub struct CommonArgs { 7 + /// Input file/directory 8 + #[arg(short, long)] 9 + pub input: Option<String>, 10 + 11 + /// Output file/directory 12 + #[arg(short, long)] 13 + pub output: Option<String>, 14 + 15 + /// Compress the input (default) 16 + #[arg(short, long)] 17 + pub compress: bool, 18 + 19 + /// Extract the input 20 + #[arg(short, long)] 21 + pub extract: bool, 22 + 23 + /// List of I/O. 24 + /// This consists of all the inputs followed by the single output, with intelligent fallback to stdin/stdout. 25 + #[arg()] 26 + pub io_list: Vec<String>, 27 + 28 + /// Ignore pipes when inferring I/O 29 + #[arg(long)] 30 + pub ignore_pipes: bool, 31 + 32 + /// Ignore stdin when inferring I/O 33 + #[arg(long)] 34 + pub ignore_stdin: bool, 35 + 36 + /// Ignore stdout when inferring I/O 37 + #[arg(long)] 38 + pub ignore_stdout: bool, 39 + } 3 40 4 41 /// Common interface for all compressor implementations 5 42 #[allow(unused_variables)]
+31 -9
src/xz.rs
··· 1 1 use crate::{ 2 - progress::{progress_bar, ProgressDisplay}, 2 + progress::{progress_bar, ProgressArgs}, 3 3 utils::*, 4 4 }; 5 + use clap::Args; 5 6 use std::{ 6 7 fs::File, 7 8 io::{self, Read, Write}, 8 9 }; 9 10 use xz2::write::{XzDecoder, XzEncoder}; 10 11 12 + #[derive(Args, Debug)] 13 + pub struct XzArgs { 14 + #[clap(flatten)] 15 + pub common_args: CommonArgs, 16 + 17 + #[clap(flatten)] 18 + progress_args: ProgressArgs, 19 + 20 + /// Level of compression. 21 + /// This is an int 0-9, with 0 being no compression and 9 being highest compression. 22 + #[arg(long, default_value_t = 6)] 23 + level: u32, 24 + } 25 + 11 26 pub struct Xz { 12 27 pub level: u32, 13 - pub progress: ProgressDisplay, 14 - pub chunk_size: usize, 28 + pub progress_args: ProgressArgs, 15 29 } 16 30 17 31 impl Default for Xz { 18 32 fn default() -> Self { 19 33 Xz { 20 34 level: 6, 21 - progress: ProgressDisplay::Auto, 22 - chunk_size: 8192, 35 + progress_args: ProgressArgs::default(), 36 + } 37 + } 38 + } 39 + 40 + impl Xz { 41 + pub fn new(args: &XzArgs) -> Xz { 42 + Xz { 43 + level: args.level, 44 + progress_args: args.progress_args, 23 45 } 24 46 } 25 47 } ··· 56 78 CmprssOutput::Pipe(pipe) => Box::new(pipe) as Box<dyn Write + Send>, 57 79 }; 58 80 let mut encoder = XzEncoder::new(output_stream, self.level); 59 - let mut bar = progress_bar(file_size, self.progress, &output); 81 + let mut bar = progress_bar(file_size, self.progress_args.progress, &output); 60 82 if let Some(progress) = &mut bar { 61 83 // Copy the input to the output in chunks so that we can update the progress bar 62 - let mut buffer = vec![0; self.chunk_size]; 84 + let mut buffer = vec![0; self.progress_args.chunk_size.size_in_bytes]; 63 85 loop { 64 86 let bytes_read = input_stream.read(&mut buffer)?; 65 87 if bytes_read == 0 { ··· 99 121 CmprssOutput::Pipe(pipe) => Box::new(pipe) as Box<dyn Write + Send>, 100 122 }; 101 123 let mut decoder = XzDecoder::new(output_stream); 102 - let mut bar = progress_bar(file_size, self.progress, &output); 124 + let mut bar = progress_bar(file_size, self.progress_args.progress, &output); 103 125 if let Some(progress) = &mut bar { 104 126 // Copy the input to the output in chunks so that we can update the progress bar 105 - let mut buffer = vec![0; self.chunk_size]; 127 + let mut buffer = vec![0; self.progress_args.chunk_size.size_in_bytes]; 106 128 loop { 107 129 let bytes_read = input_stream.read(&mut buffer)?; 108 130 if bytes_read == 0 {