-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- implement absurd arg parsing - add help text - add long help text - first iteration of compression - add --stdout - add large file support - make the compress command mostly work - make compress flags work better - verbose output works! - reduce size of zip-cli from 2.9M->1.3M - make a new subcrate clite for different features/opt flags - remove clap - set --large-file automatically - clarify info and extract are a TODO for now - move OutputHandle to lib.rs - factor out main method into lib.rs - clarify the behavior of -f and -r around symlinks - add --append option - rename CompressError -> CommandError - make much more subcommand logic generic through traits - wrap compress help to 80 chars - begin extract help text squashed commits: - implement matching logic except for --match - FINALLY fix input zips iteration - implement a couple basic transforms - add terrible ContentTransformer - add entry spec transformer - give up and use an unsafecell - impl transform_name - initial extract impl - add name matchers - impl --match - modularize extract - begin impl transforms - refactor args modules - init entry data - stub out utterly absurd cli spec - parse our absurd cli spec - impl merged input - do absurd stuff without checking for compilation success - ok it compiles now - ok it might even run correctly now? - make output files work - default to regexp for replacement and disallow globs from replacement - add --{min,max}-size match exprs - impl pattern transformers - support --transform!! - anchoring, prefixes, the whole shebang. i think we're done here - make glob and rx optional dependencies - add stub for info command - parameterize the match help text to reuse for info - stub out info format specs - write out some more help text - remove unnecessary unsafe - parse archive overview format strings - make a trait for format parsing - parse entry format - finish help text for info - add info command stub - implement basic entry info - write directly to the output stream, don't allocate a string - add escaped tab component - rename some traits and methods - pass around a reference to EntryData instead - mess with mode bits to make them look more like ls - add some verbose logs - refactor info modules - refactor parseable directive - make compiled format strings much more generic (!) - refactor modules of compiled formatting - archive format works!!!!! omg - move entry and archive iteration into helper methods - make archive formatting work fully with ArchiveData - archive data works for stdin! - add more logging to extraction - remove allocations in perms todo sorting - several preliminary notes added to extract command - make process_entry() helper method - remove UnsafeCell!!! - don't reallocate the symlink target - move symlink processing to a helper - refactor a lot of extraction - ok extract makes a lot more sense now - support --archive-comment for compression - add a TODO - make symlink creation more readable - refactor output parsing into modules - make parsing outputs much MUCH more readable with a builder - all info directives are now supported
- Loading branch information
1 parent
810d18a
commit d4041b6
Showing
24 changed files
with
7,576 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
[package] | ||
name = "zip-cli" | ||
version = "0.0.1" | ||
authors = [ | ||
"Danny McClanahan <dmcC2@hypnicjerk.ai>", | ||
] | ||
license = "MIT" | ||
repository = "https://github.com/zip-rs/zip2.git" | ||
keywords = ["zip", "archive", "compression", "cli"] | ||
categories = ["command-line-utilities", "compression", "filesystem", "development-tools::build-utils"] | ||
rust-version = "1.74.0" | ||
description = """ | ||
Binary for creation and manipulation of zip files. | ||
""" | ||
edition = "2021" | ||
|
||
# Prevent this from interfering with workspaces | ||
[workspace] | ||
members = ["."] | ||
|
||
[lib] | ||
|
||
[[bin]] | ||
name = "zip-cli" | ||
|
||
[dependencies] | ||
glob = { version = "0.3", optional = true } | ||
regex = { version = "1", optional = true } | ||
|
||
[dependencies.zip] | ||
path = ".." | ||
default-features = false | ||
|
||
[features] | ||
aes-crypto = ["zip/aes-crypto"] | ||
bzip2 = ["zip/bzip2"] | ||
chrono = ["zip/chrono"] | ||
deflate64 = ["zip/deflate64"] | ||
deflate = ["zip/deflate"] | ||
deflate-flate2 = ["zip/deflate-flate2"] | ||
deflate-zlib = ["zip/deflate-zlib"] | ||
deflate-zlib-ng = ["zip/deflate-zlib-ng"] | ||
deflate-zopfli = ["zip/deflate-zopfli"] | ||
lzma = ["zip/lzma"] | ||
time = ["zip/time"] | ||
xz = ["zip/xz"] | ||
zstd = ["zip/zstd"] | ||
|
||
glob = ["dep:glob"] | ||
rx = ["dep:regex"] | ||
|
||
default = [ | ||
"aes-crypto", | ||
"bzip2", | ||
"deflate64", | ||
"deflate", | ||
"lzma", | ||
"time", | ||
"xz", | ||
"zstd", | ||
"glob", | ||
"rx", | ||
] | ||
|
||
|
||
[profile.release] | ||
strip = true | ||
lto = true | ||
opt-level = 3 | ||
codegen-units = 1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
[package] | ||
name = "zip-clite" | ||
version = "0.0.1" | ||
authors = [ | ||
"Danny McClanahan <dmcC2@hypnicjerk.ai>", | ||
] | ||
license = "MIT" | ||
repository = "https://github.com/zip-rs/zip2.git" | ||
keywords = ["zip", "archive", "compression", "cli"] | ||
categories = ["command-line-utilities", "compression", "filesystem", "development-tools::build-utils"] | ||
rust-version = "1.74.0" | ||
description = """ | ||
Binary for creation and manipulation of zip files. | ||
""" | ||
edition = "2021" | ||
|
||
# Prevent this from interfering with workspaces | ||
[workspace] | ||
members = ["."] | ||
|
||
[[bin]] | ||
name = "zip-clite" | ||
|
||
[dependencies] | ||
|
||
[dependencies.zip-cli] | ||
path = ".." | ||
default-features = false | ||
features = ["deflate-flate2", "deflate-zlib"] | ||
|
||
[profile.release] | ||
strip = true | ||
lto = true | ||
opt-level = "s" | ||
codegen-units = 1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
fn main() { | ||
zip_cli::driver::main(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,208 @@ | ||
use std::{collections::VecDeque, ffi::OsString, fmt, sync::OnceLock}; | ||
|
||
#[derive(Debug)] | ||
pub enum ArgParseError { | ||
StdoutMessage(String), | ||
StderrMessage(String), | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct ZipCli { | ||
pub verbose: bool, | ||
pub command: ZipCommand, | ||
} | ||
|
||
#[derive(Debug)] | ||
enum SubcommandName { | ||
Compress, | ||
Info, | ||
Extract, | ||
} | ||
|
||
static PARSED_EXE_NAME: OnceLock<String> = OnceLock::new(); | ||
|
||
impl ZipCli { | ||
const VERSION: &'static str = env!("CARGO_PKG_VERSION"); | ||
const DESCRIPTION: &'static str = env!("CARGO_PKG_DESCRIPTION"); | ||
|
||
pub const INTERNAL_ERROR_EXIT_CODE: i32 = 3; | ||
pub const ARGV_PARSE_FAILED_EXIT_CODE: i32 = 2; | ||
pub const NON_FAILURE_EXIT_CODE: i32 = 0; | ||
|
||
pub fn binary_name() -> &'static str { | ||
PARSED_EXE_NAME.get().expect("binary name was not set yet") | ||
} | ||
|
||
fn generate_version_text() -> String { | ||
format!("{} {}\n", Self::binary_name(), Self::VERSION) | ||
} | ||
|
||
fn generate_usage_line() -> String { | ||
format!("Usage: {} [OPTIONS] <COMMAND>", Self::binary_name()) | ||
} | ||
|
||
fn generate_full_help_text() -> String { | ||
format!( | ||
"\ | ||
{} | ||
{} | ||
Commands: | ||
{}{}{} | ||
{}{}{} | ||
{}{}{} | ||
Options: | ||
-v, --verbose Write information logs to stderr | ||
-h, --help Print help | ||
-V, --version Print version | ||
", | ||
Self::DESCRIPTION, | ||
Self::generate_usage_line(), | ||
compress::Compress::COMMAND_NAME, | ||
compress::Compress::COMMAND_TABS, | ||
compress::Compress::COMMAND_DESCRIPTION, | ||
info::Info::COMMAND_NAME, | ||
info::Info::COMMAND_TABS, | ||
info::Info::COMMAND_DESCRIPTION, | ||
extract::Extract::COMMAND_NAME, | ||
extract::Extract::COMMAND_TABS, | ||
extract::Extract::COMMAND_DESCRIPTION, | ||
) | ||
} | ||
|
||
fn generate_brief_help_text(context: &str) -> String { | ||
format!( | ||
"\ | ||
error: {context} | ||
{} | ||
For more information, try '--help'. | ||
", | ||
Self::generate_usage_line() | ||
) | ||
} | ||
|
||
fn parse_up_to_subcommand_name( | ||
argv: &mut VecDeque<OsString>, | ||
) -> Result<(bool, SubcommandName), ArgParseError> { | ||
let mut verbose: bool = false; | ||
let mut subcommand_name: Option<SubcommandName> = None; | ||
while subcommand_name.is_none() { | ||
match argv.pop_front() { | ||
None => { | ||
let help_text = Self::generate_full_help_text(); | ||
return Err(ArgParseError::StderrMessage(help_text)); | ||
} | ||
Some(arg) => match arg.as_encoded_bytes() { | ||
b"-v" | b"--verbose" => verbose = true, | ||
b"-V" | b"--version" => { | ||
let version_text = Self::generate_version_text(); | ||
return Err(ArgParseError::StdoutMessage(version_text)); | ||
} | ||
b"-h" | b"--help" => { | ||
let help_text = Self::generate_full_help_text(); | ||
return Err(ArgParseError::StdoutMessage(help_text)); | ||
} | ||
b"compress" => subcommand_name = Some(SubcommandName::Compress), | ||
b"info" => subcommand_name = Some(SubcommandName::Info), | ||
b"extract" => subcommand_name = Some(SubcommandName::Extract), | ||
arg_bytes => { | ||
let context = if arg_bytes.starts_with(b"-") { | ||
format!("unrecognized global flag {arg:?}") | ||
} else { | ||
format!("unrecognized subcommand name {arg:?}") | ||
}; | ||
let help_text = Self::generate_brief_help_text(&context); | ||
return Err(ArgParseError::StderrMessage(help_text)); | ||
} | ||
}, | ||
} | ||
} | ||
Ok((verbose, subcommand_name.unwrap())) | ||
} | ||
|
||
pub fn parse_argv(argv: impl IntoIterator<Item = OsString>) -> Result<Self, ArgParseError> { | ||
let mut argv: VecDeque<OsString> = argv.into_iter().collect(); | ||
let exe_name: String = argv | ||
.pop_front() | ||
.expect("exe name not on command line") | ||
.into_string() | ||
.expect("exe name not valid unicode"); | ||
PARSED_EXE_NAME | ||
.set(exe_name) | ||
.expect("exe name already written"); | ||
let (verbose, subcommand_name) = Self::parse_up_to_subcommand_name(&mut argv)?; | ||
let command = match subcommand_name { | ||
SubcommandName::Info => ZipCommand::Info(info::Info::parse_argv(argv)?), | ||
SubcommandName::Extract => ZipCommand::Extract(extract::Extract::parse_argv(argv)?), | ||
SubcommandName::Compress => ZipCommand::Compress(compress::Compress::parse_argv(argv)?), | ||
}; | ||
Ok(Self { verbose, command }) | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub enum ZipCommand { | ||
Compress(compress::Compress), | ||
Info(info::Info), | ||
Extract(extract::Extract), | ||
} | ||
|
||
pub trait CommandFormat: fmt::Debug { | ||
const COMMAND_NAME: &'static str; | ||
const COMMAND_TABS: &'static str; | ||
const COMMAND_DESCRIPTION: &'static str; | ||
|
||
const USAGE_LINE: &'static str; | ||
|
||
fn generate_usage_line() -> String { | ||
format!( | ||
"Usage: {} {} {}", | ||
ZipCli::binary_name(), | ||
Self::COMMAND_NAME, | ||
Self::USAGE_LINE, | ||
) | ||
} | ||
|
||
fn generate_help() -> String; | ||
|
||
fn generate_full_help_text() -> String { | ||
format!( | ||
"\ | ||
{} | ||
{} | ||
{}", | ||
Self::COMMAND_DESCRIPTION, | ||
Self::generate_usage_line(), | ||
Self::generate_help(), | ||
) | ||
} | ||
|
||
fn generate_brief_help_text(context: &str) -> String { | ||
format!( | ||
"\ | ||
error: {context} | ||
{} | ||
", | ||
Self::generate_usage_line() | ||
) | ||
} | ||
|
||
fn exit_arg_invalid(context: &str) -> ArgParseError { | ||
let message = Self::generate_brief_help_text(context); | ||
ArgParseError::StderrMessage(message) | ||
} | ||
|
||
fn parse_argv(argv: VecDeque<OsString>) -> Result<Self, ArgParseError> | ||
where | ||
Self: Sized; | ||
} | ||
|
||
pub mod compress; | ||
pub mod extract; | ||
pub mod info; |
Oops, something went wrong.