this repo has no description
0
fork

Configure Feed

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

at 45b6ec32e5c393e299f0387ba320b08e24b39ecb 189 lines 5.5 kB view raw
1use clap::Args; 2use std::ffi::OsStr; 3use std::fmt; 4use std::io; 5use std::path::{Path, PathBuf}; 6use std::str::FromStr; 7 8#[derive(Args, Debug)] 9pub struct CommonArgs { 10 /// Input file/directory 11 #[arg(short, long)] 12 pub input: Option<String>, 13 14 /// Output file/directory 15 #[arg(short, long)] 16 pub output: Option<String>, 17 18 /// Compress the input (default) 19 #[arg(short, long)] 20 pub compress: bool, 21 22 /// Extract the input 23 #[arg(short, long)] 24 pub extract: bool, 25 26 /// Decompress the input. Alias of --extract 27 #[arg(short, long)] 28 pub decompress: bool, 29 30 /// List of I/O. 31 /// This consists of all the inputs followed by the single output, with intelligent fallback to stdin/stdout. 32 #[arg()] 33 pub io_list: Vec<String>, 34 35 /// Ignore pipes when inferring I/O 36 #[arg(long)] 37 pub ignore_pipes: bool, 38 39 /// Ignore stdin when inferring I/O 40 #[arg(long)] 41 pub ignore_stdin: bool, 42 43 /// Ignore stdout when inferring I/O 44 #[arg(long)] 45 pub ignore_stdout: bool, 46} 47 48#[derive(Debug, Clone, Copy)] 49pub struct CompressionLevel { 50 pub level: u32, 51} 52 53impl Default for CompressionLevel { 54 fn default() -> Self { 55 CompressionLevel { level: 6 } 56 } 57} 58 59impl FromStr for CompressionLevel { 60 type Err = &'static str; 61 62 fn from_str(s: &str) -> Result<Self, Self::Err> { 63 // Check for an int 64 if let Ok(level) = s.parse::<u32>() { 65 if level < 10 { 66 return Ok(CompressionLevel { level }); 67 } else { 68 return Err("Compression level must be 0-9"); 69 } 70 } 71 let s = s.to_lowercase(); 72 match s.as_str() { 73 "none" => Ok(CompressionLevel { level: 0 }), 74 "fast" => Ok(CompressionLevel { level: 1 }), 75 "best" => Ok(CompressionLevel { level: 9 }), 76 _ => Err("Invalid compression level"), 77 } 78 } 79} 80 81#[derive(Args, Debug, Default, Clone, Copy)] 82pub struct LevelArgs { 83 /// Level of compression. 84 /// This is an int 0-9, with 0 being no compression and 9 being highest compression. 85 /// Also supports 'none', 'fast', and 'best'. 86 #[arg(long, default_value = "6")] 87 pub level: CompressionLevel, 88} 89 90/// Common interface for all compressor implementations 91#[allow(unused_variables)] 92pub trait Compressor { 93 /// Name of this Compressor 94 fn name(&self) -> &str; 95 96 /// Default extension for this Compressor 97 fn extension(&self) -> &str { 98 self.name() 99 } 100 101 /// Detect if the input is an archive of this type 102 /// Just checks the extension by default 103 /// Some compressors may overwrite this to do more advanced detection 104 fn is_archive(&self, in_path: &Path) -> bool { 105 if in_path.extension().is_none() { 106 return false; 107 } 108 in_path.extension().unwrap() == self.extension() 109 } 110 111 /// Generate the default name for the compressed file 112 fn default_compressed_filename(&self, in_path: &Path) -> String { 113 format!( 114 "{}.{}", 115 in_path 116 .file_name() 117 .unwrap_or_else(|| OsStr::new("archive")) 118 .to_str() 119 .unwrap(), 120 self.extension() 121 ) 122 } 123 124 /// Generate the default extracted filename 125 fn default_extracted_filename(&self, in_path: &Path) -> String { 126 // If the file has the extension for this type, return the filename without the extension 127 if in_path.extension().unwrap() == self.extension() { 128 return in_path.file_stem().unwrap().to_str().unwrap().to_string(); 129 } 130 // If the file has no extension, return the current directory 131 if in_path.extension().is_none() { 132 return ".".to_string(); 133 } 134 // Otherwise, return the current directory and hope for the best 135 ".".to_string() 136 } 137 138 fn compress(&self, input: CmprssInput, output: CmprssOutput) -> Result<(), io::Error> { 139 cmprss_error("compress_target unimplemented") 140 } 141 142 fn extract(&self, input: CmprssInput, output: CmprssOutput) -> Result<(), io::Error> { 143 cmprss_error("extract_target unimplemented") 144 } 145} 146 147impl fmt::Debug for dyn Compressor { 148 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 149 write!(f, "Compressor {{ name: {} }}", self.name()) 150 } 151} 152 153pub fn cmprss_error(message: &str) -> Result<(), io::Error> { 154 Err(io::Error::new(io::ErrorKind::Other, message)) 155} 156 157/// Defines the possible inputs of a compressor 158#[derive(Debug)] 159pub enum CmprssInput { 160 /// Path(s) to the input files. 161 Path(Vec<PathBuf>), 162 /// Input pipe 163 Pipe(std::io::Stdin), 164} 165 166/// Defines the possible outputs of a compressor 167#[derive(Debug)] 168pub enum CmprssOutput { 169 Path(PathBuf), 170 Pipe(std::io::Stdout), 171} 172 173#[cfg(test)] 174mod tests { 175 use super::*; 176 177 #[test] 178 fn compression_level_parsing() { 179 assert_eq!(CompressionLevel::from_str("0").unwrap().level, 0); 180 assert_eq!(CompressionLevel::from_str("1").unwrap().level, 1); 181 assert_eq!(CompressionLevel::from_str("9").unwrap().level, 9); 182 assert_eq!(CompressionLevel::from_str("none").unwrap().level, 0); 183 assert_eq!(CompressionLevel::from_str("fast").unwrap().level, 1); 184 assert_eq!(CompressionLevel::from_str("best").unwrap().level, 9); 185 assert!(CompressionLevel::from_str("-1").is_err()); 186 assert!(CompressionLevel::from_str("10").is_err()); 187 assert!(CompressionLevel::from_str("foo").is_err()); 188 } 189}