this repo has no description
0
fork

Configure Feed

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

refactor: standardize the interface for compressors

+186 -134
+12 -28
src/gzip.rs
··· 1 1 use crate::utils::*; 2 2 use flate2::write::GzEncoder; 3 3 use flate2::{read::GzDecoder, Compression}; 4 - use std::fs::File; 5 - use std::io::{Read, Write}; 6 - use std::path::Path; 4 + use std::io::{self, Read, Write}; 7 5 8 6 pub struct Gzip { 9 7 pub compression_level: u32, 10 8 pub common_args: CmprssCommonArgs, 11 9 } 12 10 13 - impl CmprssArgTrait for Gzip { 14 - fn common_args(&self) -> &CmprssCommonArgs { 15 - &self.common_args 16 - } 17 - } 18 - 19 - impl CmprssInfo for Gzip { 11 + impl Compressor for Gzip { 20 12 /// The standard extension for the gzip format. 21 13 fn extension(&self) -> &str { 22 14 "gz" ··· 26 18 fn name(&self) -> &str { 27 19 "gzip" 28 20 } 29 - } 30 21 31 - impl CmprssFile for Gzip { 32 - /// Compress an input file to gzip 33 - fn compress_file<I: AsRef<Path>, O: Write>(&self, in_file: I, output: O) { 34 - self.compress(File::open(in_file).unwrap(), output); 35 - } 36 - 37 - /// Extract a gzipped file 38 - fn extract_file<I: AsRef<Path>, O: Write>(&self, in_file: I, output: O) { 39 - self.extract(File::open(in_file).unwrap(), output); 22 + fn common_args(&self) -> &CmprssCommonArgs { 23 + &self.common_args 40 24 } 41 - } 42 25 43 - impl CmprssRead for Gzip { 44 - /// Compress an input file or directory into a gzip archive. 45 - fn compress<I: Read, O: Write>(&self, mut input: I, output: O) { 26 + /// Compress an input stream into a gzip archive. 27 + fn compress<I: Read, O: Write>(&self, mut input: I, output: O) -> Result<(), io::Error> { 46 28 let mut encoder = GzEncoder::new(output, Compression::new(self.compression_level)); 47 29 48 - std::io::copy(&mut input, &mut encoder).unwrap(); 49 - encoder.finish().unwrap(); 30 + std::io::copy(&mut input, &mut encoder)?; 31 + encoder.finish()?; 32 + Ok(()) 50 33 } 51 34 52 35 /// Extract the gzip compressed data 53 - fn extract<I: Read, O: Write>(&self, input: I, mut output: O) { 36 + fn extract<I: Read, O: Write>(&self, input: I, mut output: O) -> Result<(), io::Error> { 54 37 let mut decoder = GzDecoder::new(input); 55 - std::io::copy(&mut decoder, &mut output).unwrap(); 38 + std::io::copy(&mut decoder, &mut output)?; 39 + Ok(()) 56 40 } 57 41 }
+29 -47
src/main.rs
··· 3 3 mod utils; 4 4 5 5 use clap::{Args, Parser, Subcommand}; 6 - use std::fs::File; 6 + use std::io; 7 7 use std::path::Path; 8 8 use utils::*; 9 9 ··· 105 105 } 106 106 } 107 107 108 - /// Execute a tar command 109 - fn command_tar(args: TarArgs) { 110 - let args = args.common_args; 108 + /// Compress using the compressor 109 + fn compress_generic<T: Compressor>(compressor: T) -> Result<(), io::Error> { 110 + // TODO: Properly handle the output file 111 + // Fail/Warn on existence 112 + // Remove if you've created a stub 113 + let args = compressor.common_args(); 111 114 let input_path = Path::new(&args.input); 112 - if args.compress { 113 - let out = output_filename(input_path, &args.output, tar::EXT); 114 - tar::compress_file(input_path, File::create(out).unwrap()); 115 - } else if args.extract { 116 - tar::extract_file(input_path, args.output.unwrap_or(".".to_string())); 117 - } else { 118 - // Neither is set. 119 - // Compress by default, warn if if looks like an archive. 120 - if input_path.extension().unwrap() == tar::EXT { 121 - println!( 122 - "error: input appears to already be a tar archive, exiting. Use '--compress' if needed." 123 - ) 124 - } else { 125 - let out = output_filename(input_path, &args.output, tar::EXT); 126 - tar::compress_file(input_path, File::create(out).unwrap()); 127 - } 128 - } 129 - } 130 - 131 - /// Execute an extract command. 132 - /// 133 - /// Attempts to extract based on the file extension. 134 - fn command_extract(args: ExtractArgs) { 135 - let input_path = Path::new(&args.input); 136 - match input_path.extension().unwrap().to_str().unwrap() { 137 - tar::EXT => tar::extract_file(input_path, args.output.unwrap_or(".".to_string())), 138 - _ => println!("error: unknown format "), 139 - } 115 + let out = output_filename(input_path, &args.output, compressor.extension()); 116 + compressor.compress_path_to_path(input_path, out) 140 117 } 141 118 142 119 /// Implement compression/extraction with a generic Compressor. 143 - fn command_generic<T: CmprssArgTrait + CmprssRead + CmprssInfo>(compressor: T) { 120 + fn command_generic<T: Compressor>(compressor: T) -> Result<(), io::Error> { 144 121 let args = compressor.common_args(); 145 122 let input_path = Path::new(&args.input); 146 123 if args.compress { 147 - let out = output_filename(input_path, &args.output, compressor.extension()); 148 - compressor.compress(File::open(input_path).unwrap(), File::create(out).unwrap()); 124 + compress_generic(compressor)?; 149 125 } else if args.extract { 150 - assert!(args.output.is_some(), "error: output filename required"); 151 - compressor.extract( 152 - File::open(input_path).unwrap(), 153 - File::create(args.output.clone().unwrap()).unwrap(), 154 - ); 126 + let output = match args.output.as_ref() { 127 + Some(out) => out, 128 + None => ".", 129 + }; 130 + compressor.extract_path_to_path(input_path, output)?; 155 131 } else { 156 132 // Neither is set. 157 133 // Compress by default, warn if if looks like an archive. ··· 160 136 "error: input appears to already be a {} archive, exiting. Use '--compress' if needed.", compressor.name() 161 137 ) 162 138 } else { 163 - let out = output_filename(input_path, &args.output, compressor.extension()); 164 - compressor.compress(File::open(input_path).unwrap(), File::create(out).unwrap()); 139 + compress_generic(compressor)?; 165 140 } 166 141 } 142 + Ok(()) 167 143 } 168 144 169 145 fn parse_gzip(args: GzipArgs) -> gzip::Gzip { ··· 173 149 } 174 150 } 175 151 176 - fn main() { 152 + fn parse_tar(args: TarArgs) -> tar::Tar { 153 + tar::Tar { 154 + common_args: args.common_args.into_common(), 155 + } 156 + } 157 + 158 + fn main() -> Result<(), io::Error> { 177 159 let args = CmprssArgs::parse(); 178 160 match args.format { 179 - Some(Format::Tar(a)) => command_tar(a), 180 - Some(Format::Extract(a)) => command_extract(a), 161 + Some(Format::Tar(a)) => command_generic(parse_tar(a)), 162 + //Some(Format::Extract(a)) => command_extract(a), 181 163 Some(Format::Gzip(a)) => command_generic(parse_gzip(a)), 182 - None => println!("none"), 183 - }; 164 + _ => Err(io::Error::new(io::ErrorKind::Other, "unknown input")), 165 + } 184 166 }
+54 -36
src/tar.rs
··· 1 1 extern crate tar; 2 2 3 3 use std::fs::File; 4 - use std::io::{Read, Write}; 4 + use std::io::{self, Read, Write}; 5 5 use std::path::Path; 6 6 use tar::{Archive, Builder}; 7 7 8 - /// The standard extension for the tar format. 9 - pub const EXT: &str = "tar"; 8 + use crate::utils::*; 10 9 11 - /// Compress an input file or directory into a tar archive. 12 - pub fn compress_file<I: AsRef<Path>, O: Write>(in_file: I, output: O) { 13 - let in_file = in_file.as_ref(); 14 - println!("tar: Compressing {}", in_file.display()); 15 - let mut archive = Builder::new(output); //File::create(out_file).unwrap()); 16 - if in_file.is_file() { 17 - archive 18 - .append_file( 19 - in_file.file_name().unwrap(), 20 - &mut File::open(in_file).unwrap(), 21 - ) 22 - .unwrap(); 23 - } else if in_file.is_dir() { 24 - archive 25 - .append_dir_all(in_file.file_name().unwrap(), in_file) 26 - .unwrap(); 27 - } 28 - archive.finish().unwrap(); 10 + pub struct Tar { 11 + pub common_args: CmprssCommonArgs, 29 12 } 30 13 31 - /// Extract the archive file into a directory 32 - pub fn extract_file<I: AsRef<Path>, O: AsRef<Path>>(input_file: I, out_directory: O) { 33 - let input_file = input_file.as_ref(); 34 - let out_directory = out_directory.as_ref(); 35 - println!( 36 - "tar: Extracting {} into {}", 37 - input_file.display(), 38 - out_directory.display() 39 - ); 40 - extract(File::open(input_file).unwrap(), out_directory); 41 - } 14 + impl Compressor for Tar { 15 + /// Full name for tar, also used for extension 16 + fn name(&self) -> &str { 17 + "tar" 18 + } 42 19 43 - /// Extract the archive into a directory 44 - pub fn extract<I: Read, O: AsRef<Path>>(input: I, out_directory: O) { 45 - let mut archive = Archive::new(input); 46 - archive.unpack(out_directory.as_ref()).unwrap(); 20 + fn common_args(&self) -> &CmprssCommonArgs { 21 + &self.common_args 22 + } 23 + 24 + /// Compress an input file or directory into a tar archive. 25 + fn compress_file<I: AsRef<Path>, O: Write>( 26 + &self, 27 + in_file: I, 28 + output: O, 29 + ) -> Result<(), io::Error> { 30 + let in_file = in_file.as_ref(); 31 + println!("tar: Compressing {}", in_file.display()); 32 + let mut archive = Builder::new(output); 33 + if in_file.is_file() { 34 + archive.append_file(in_file.file_name().unwrap(), &mut File::open(in_file)?)?; 35 + } else if in_file.is_dir() { 36 + archive.append_dir_all(in_file.file_name().unwrap(), in_file)?; 37 + } else { 38 + return Err(io::Error::new( 39 + io::ErrorKind::InvalidInput, 40 + "unknown file type", 41 + )); 42 + } 43 + archive.finish() 44 + } 45 + 46 + /// Extract one path to another path 47 + fn extract_path_to_path<I: AsRef<Path>, O: AsRef<Path>>( 48 + &self, 49 + in_file: I, 50 + out_file: O, 51 + ) -> Result<(), io::Error> { 52 + self.extract_to_path(File::open(in_file)?, out_file) 53 + } 54 + 55 + /// Extract the archive into a directory 56 + fn extract_to_path<I: Read, O: AsRef<Path>>( 57 + &self, 58 + input: I, 59 + out_path: O, 60 + ) -> Result<(), io::Error> { 61 + println!("tar extracting to {}", out_path.as_ref().display()); 62 + let mut archive = Archive::new(input); 63 + archive.unpack(out_path.as_ref()) 64 + } 47 65 }
+91 -23
src/utils.rs
··· 1 - use std::io::{Read, Write}; 1 + use std::fs::File; 2 + use std::io::{self, Read, Write}; 2 3 use std::path::Path; 3 4 4 - /// Trait for generic compression/extract over Read/Write objects 5 - pub trait CmprssRead { 6 - fn compress<I: Read, O: Write>(&self, input: I, output: O); 7 - fn extract<I: Read, O: Write>(&self, input: I, output: O); 8 - } 9 - 10 - /// Trait for compressing/extracting from a file. 11 - pub trait CmprssFile { 12 - /// Compress an input file 13 - fn compress_file<I: AsRef<Path>, O: Write>(&self, in_file: I, output: O); 14 - 15 - /// Extract a file 16 - fn extract_file<I: AsRef<Path>, O: Write>(&self, in_file: I, output: O); 17 - } 18 - 19 5 pub struct CmprssCommonArgs { 20 6 pub compress: bool, 21 7 pub extract: bool, ··· 23 9 pub output: Option<String>, 24 10 } 25 11 26 - /// Getter method for the common arguments 27 - pub trait CmprssArgTrait { 12 + /// Common interface for all compressor implementations 13 + #[allow(unused_variables)] 14 + pub trait Compressor { 15 + /// Name of this Compressor 16 + fn name(&self) -> &str; 17 + 18 + /// Default extension for this Compressor 19 + fn extension(&self) -> &str { 20 + self.name() 21 + } 22 + 23 + /// Getter method for the common arguments 28 24 // TODO: There is probably a cleaner way to do this? 29 25 fn common_args(&self) -> &CmprssCommonArgs; 30 - } 31 26 32 - /// Generic info about the given compressor. 33 - pub trait CmprssInfo { 34 - fn extension(&self) -> &str; 35 - fn name(&self) -> &str; 27 + /// Compress one path to another path 28 + fn compress_path_to_path<I: AsRef<Path>, O: AsRef<Path>>( 29 + &self, 30 + in_file: I, 31 + out_file: O, 32 + ) -> Result<(), io::Error> { 33 + println!("compress_path_to_path"); 34 + self.compress_file(in_file, File::create(out_file)?) 35 + } 36 + 37 + /// Compress an input filename to a stream 38 + fn compress_file<I: AsRef<Path>, O: Write>( 39 + &self, 40 + in_file: I, 41 + output: O, 42 + ) -> Result<(), io::Error> { 43 + println!("compress_file"); 44 + self.compress(File::open(in_file)?, output) 45 + } 46 + 47 + /// Extract one path to another path 48 + fn extract_path_to_path<I: AsRef<Path>, O: AsRef<Path>>( 49 + &self, 50 + in_file: I, 51 + out_file: O, 52 + ) -> Result<(), io::Error> { 53 + self.extract_file(in_file, File::create(out_file)?) 54 + } 55 + 56 + /// Extract an input filename to a stream 57 + fn extract_file<I: AsRef<Path>, O: Write>( 58 + &self, 59 + in_file: I, 60 + output: O, 61 + ) -> Result<(), io::Error> { 62 + self.extract(File::open(in_file)?, output) 63 + } 64 + 65 + /// Compress a Read trait object to a Write object. 66 + fn compress<I: Read, O: Write>(&self, input: I, output: O) -> Result<(), io::Error> { 67 + Err(io::Error::new( 68 + io::ErrorKind::Other, 69 + "compress unimplemented", 70 + )) 71 + } 72 + 73 + /// Extract a Read trait object to a Write object. 74 + fn extract<I: Read, O: Write>(&self, input: I, output: O) -> Result<(), io::Error> { 75 + Err(io::Error::new( 76 + io::ErrorKind::Other, 77 + "extract unimplemented", 78 + )) 79 + } 80 + 81 + /// Extract a Read trait object to a path. 82 + /// Some compressors require this instead of writing to a stream 83 + fn extract_to_path<I: Read, O: AsRef<Path>>( 84 + &self, 85 + input: I, 86 + out_path: O, 87 + ) -> Result<(), io::Error> { 88 + Err(io::Error::new( 89 + io::ErrorKind::Other, 90 + "extract_to_path unimplemented", 91 + )) 92 + } 93 + 94 + /// Extract a file to a path 95 + fn extract_file_to_path<I: AsRef<Path>, O: AsRef<Path>>( 96 + &self, 97 + input_file: I, 98 + out_directory: O, 99 + ) -> Result<(), io::Error> { 100 + let input_file = input_file.as_ref(); 101 + let out_directory = out_directory.as_ref(); 102 + self.extract_to_path(File::open(input_file)?, out_directory) 103 + } 36 104 }