this repo has no description
0
fork

Configure Feed

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

feat(cli): accept compound format prefix like `tar.gz` as first arg

+127 -1
+10
README.md
··· 131 131 | `.txz` | `.tar.xz` | 132 132 | `.tzst` | `.tar.zst` | 133 133 134 + You can also pass a compound format as the leading argument (like the `tar` subcommand) to make it explicit, without writing out a target filename: 135 + 136 + ```bash 137 + # Compress a directory to directory.tar.gz 138 + cmprss tar.gz directory 139 + 140 + # Same, using the shortcut form 141 + cmprss tgz directory out.tgz 142 + ``` 143 + 134 144 Pipes can still be used if preferred: 135 145 136 146 ```bash
+12
src/backends/mod.rs
··· 63 63 _ => compressor_from_str(ext).map(|c| vec![c]), 64 64 } 65 65 } 66 + 67 + /// Resolve a dotted format string (e.g. `tar.gz`, `tgz`, `xz`) into a 68 + /// compressor chain. Every dot-separated segment is resolved via 69 + /// `chain_from_ext` and concatenated in order. Returns `None` if any 70 + /// segment isn't a known codec or shortcut. 71 + pub fn chain_from_format_str(s: &str) -> Option<Vec<Box<dyn Compressor>>> { 72 + let mut chain = Vec::new(); 73 + for part in s.split('.') { 74 + chain.extend(chain_from_ext(part)?); 75 + } 76 + if chain.is_empty() { None } else { Some(chain) } 77 + }
+20 -1
src/main.rs
··· 78 78 Manpage, 79 79 } 80 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. 86 + fn 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 + io_list.remove(0); 93 + Some(Box::new(Pipeline::new(chain))) 94 + } 95 + 81 96 fn write_completions(shell: Shell) -> Result { 82 97 let mut cmd = CmprssArgs::command(); 83 98 clap_complete::generate(shell, &mut cmd, "cmprss", &mut std::io::stdout()); ··· 115 130 Some(Format::SevenZ(a)) => command(Some(Box::new(SevenZ::new(&a))), &a.common_args), 116 131 Some(Format::Completions { shell }) => write_completions(shell), 117 132 Some(Format::Manpage) => write_manpage(), 118 - None => command(None, &args.base_args), 133 + None => { 134 + let mut base_args = args.base_args; 135 + let compressor = take_format_prefix(&mut base_args.io_list); 136 + command(compressor, &base_args) 137 + } 119 138 } 120 139 .unwrap_or_else(|e| { 121 140 eprintln!("ERROR(cmprss): {}", e);
+85
tests/shortcuts.rs
··· 68 68 shortcut_roundtrip("tzst") 69 69 } 70 70 } 71 + 72 + /// Roundtrip helper: pass `format` as the leading positional (e.g. 73 + /// `cmprss tar.gz src/ out.tar.gz`), then extract with the same format 74 + /// and compare contents. 75 + fn format_prefix_roundtrip( 76 + format: &str, 77 + archive_ext: &str, 78 + ) -> Result<(), Box<dyn std::error::Error>> { 79 + let file = create_test_file("test.txt", "garbage data for testing")?; 80 + let file2 = create_test_file("test2.txt", "more garbage data for testing")?; 81 + let working_dir = create_working_dir()?; 82 + let archive_name = format!("archive.{archive_ext}"); 83 + let archive = working_dir.child(&archive_name); 84 + archive.assert(predicate::path::missing()); 85 + 86 + let mut compress = Command::cargo_bin("cmprss")?; 87 + compress 88 + .current_dir(&working_dir) 89 + .arg("--ignore-pipes") 90 + .arg(format) 91 + .arg(file.path()) 92 + .arg(file2.path()) 93 + .arg(&archive_name); 94 + compress.assert().success(); 95 + archive.assert(predicate::path::is_file()); 96 + 97 + let extract_dir = create_working_dir()?; 98 + let mut extract = Command::cargo_bin("cmprss")?; 99 + extract 100 + .current_dir(&extract_dir) 101 + .arg("--ignore-pipes") 102 + .arg(format) 103 + .arg("--extract") 104 + .arg(archive.path()); 105 + extract.assert().success(); 106 + 107 + assert_files_equal(file.path(), &extract_dir.child("test.txt")); 108 + assert_files_equal(file2.path(), &extract_dir.child("test2.txt")); 109 + 110 + Ok(()) 111 + } 112 + 113 + mod format_prefix { 114 + use super::*; 115 + 116 + #[test] 117 + fn tar_gz() -> Result<(), Box<dyn std::error::Error>> { 118 + format_prefix_roundtrip("tar.gz", "tar.gz") 119 + } 120 + 121 + #[test] 122 + fn tgz() -> Result<(), Box<dyn std::error::Error>> { 123 + format_prefix_roundtrip("tgz", "tgz") 124 + } 125 + 126 + #[test] 127 + fn tar_xz() -> Result<(), Box<dyn std::error::Error>> { 128 + format_prefix_roundtrip("tar.xz", "tar.xz") 129 + } 130 + 131 + #[test] 132 + fn txz() -> Result<(), Box<dyn std::error::Error>> { 133 + format_prefix_roundtrip("txz", "txz") 134 + } 135 + 136 + #[test] 137 + fn tar_bz2() -> Result<(), Box<dyn std::error::Error>> { 138 + format_prefix_roundtrip("tar.bz2", "tar.bz2") 139 + } 140 + 141 + #[test] 142 + fn tbz2() -> Result<(), Box<dyn std::error::Error>> { 143 + format_prefix_roundtrip("tbz2", "tbz2") 144 + } 145 + 146 + #[test] 147 + fn tar_zst() -> Result<(), Box<dyn std::error::Error>> { 148 + format_prefix_roundtrip("tar.zst", "tar.zst") 149 + } 150 + 151 + #[test] 152 + fn tzst() -> Result<(), Box<dyn std::error::Error>> { 153 + format_prefix_roundtrip("tzst", "tzst") 154 + } 155 + }