Skip to content

Commit

Permalink
more stub of extract
Browse files Browse the repository at this point in the history
  • Loading branch information
cosmicexplorer committed Aug 21, 2024
1 parent 198eda9 commit 3731b9e
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 26 deletions.
181 changes: 156 additions & 25 deletions cli/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,9 +421,13 @@ Positional entries:

/* Output flags */
b"--stdout" => {
if output_path.is_some() {
if let Some(output_path) = output_path.take() {
return Err(Self::exit_arg_invalid(&format!(
"--stdout provided along with output file {output_path:?}"
)));
} else if append_to_output_path {
return Err(Self::exit_arg_invalid(
"--stdout provided along with output file",
"--stdout provided along with --append",
));
} else if !args.is_empty() || !positional_paths.is_empty() {
return Err(Self::exit_arg_invalid("--stdout provided after entries"));
Expand All @@ -447,8 +451,13 @@ Positional entries:
}
}
b"-o" | b"--output-file" => {
if output_path.is_some() {
return Err(Self::exit_arg_invalid("--output-file provided twice"));
let new_path = argv.pop_front().map(PathBuf::from).ok_or_else(|| {
Self::exit_arg_invalid("no argument provided for -o/--output-file")
})?;
if let Some(prev_path) = output_path.take() {
return Err(Self::exit_arg_invalid(&format!(
"--output-file provided twice: {prev_path:?} and {new_path:?}"
)));
} else if allow_stdout {
return Err(Self::exit_arg_invalid(
"--stdout provided along with output file",
Expand All @@ -458,16 +467,7 @@ Positional entries:
"-o/--output-file provided after entries",
));
} else {
match argv.pop_front() {
Some(path) => {
output_path = Some(path.into());
}
None => {
return Err(Self::exit_arg_invalid(
"no argument provided for -o/--output-file",
));
}
}
output_path = Some(new_path);
}
}

Expand Down Expand Up @@ -684,14 +684,27 @@ pub mod extract {
#[derive(Debug)]
pub enum OutputCollation {
ConcatenateStdout,
Filesystem { output_dir: PathBuf, mkdir: bool },
Filesystem {
output_dir: Option<PathBuf>,
mkdir: bool,
},
}

#[derive(Debug)]
pub enum InputType {
StreamingStdin,
ZipPaths(Vec<PathBuf>),
}

#[derive(Debug)]
pub struct Extract {
pub collation: Option<OutputCollation>,
pub output: OutputCollation,
pub args: Vec<ExtractArg>,
pub positional_zips: Vec<PathBuf>,
pub input: InputType,
}

impl Extract {
const PATTERN_SPEC: &'static str = "[:glob|:lit|:rx[:i]]";
}

impl CommandFormat for Extract {
Expand All @@ -700,17 +713,18 @@ pub mod extract {
const COMMAND_DESCRIPTION: &'static str =
"Extract individual entries or an entire archive into a stream or the filesystem.";

/* TODO: support reading a zip from stdin! It avoids some extraction optimizations, but we
* can use the streaming API! */
const USAGE_LINE: &'static str =
"[-h|--help] [OUTPUT-FLAGS] [ENTRY-SPECS]... [--stdin|[--] ZIP-PATH...]";

fn generate_help() -> String {
"
let pattern_spec = Self::PATTERN_SPEC;
format!(
"
-h, --help Print help
Output flags:
Where and how to collate the extracted entries.
-d, --output-directory <dir>
Output directory path to write extracted entries into.
Paths for extracted entries will be constructed by interpreting entry
Expand All @@ -734,8 +748,32 @@ Where and how to collate the extracted entries.
This will write output to stdout even if stdout is a tty.
ENTRY SPECS:
After output flags are provided, entry specs are processed in order until an
input argument is reached. Attributes modify later entry patterns.
Sticky attributes:
These flags apply to every entry pattern that comes after them until reset by
another instance of the same attribute.
-a, --strip-components <num>
-a, --remove-prefix{pattern_spec} <pattern>
-a, --transform{pattern_spec} <pattern> <replacement-spec>
-a, --add-prefix <prefix>
Non-sticky attributes:
These flags only apply to the next entry pattern after them, and may not
be repeated.
-a, --not
Entry patterns:
???
-a, --type [file|dir|symlink]
-a, --max-depth <num>
-a, --min-depth <num>
-a, --path{pattern_spec} <pattern>
-a, --name{pattern_spec} <pattern>
Input arguments:
Zip file inputs to extract from can be specified in exactly one of two ways:
Expand All @@ -758,12 +796,15 @@ Positional paths:
all provided paths must exist and point to an existing zip file. Pipes
are not supported and will produce an error.
"
.to_string()
)
}

fn parse_argv(mut argv: VecDeque<OsString>) -> Result<Self, ArgParseError> {
let mut collation: Option<OutputCollation> = None;
let mut output_dir: Option<PathBuf> = None;
let mut mkdir_flag: bool = false;
let mut stdout_flag: bool = false;
let mut args: Vec<ExtractArg> = Vec::new();
let mut stdin_flag: bool = false;
let mut positional_zips: Vec<PathBuf> = Vec::new();

while let Some(arg) = argv.pop_front() {
Expand All @@ -773,7 +814,72 @@ Positional paths:
return Err(ArgParseError::StdoutMessage(help_text));
}

/* Transition to positional args */
/* Output args */
b"-d" | b"--output-directory" => {
let new_path = argv.pop_front().map(PathBuf::from).ok_or_else(|| {
Self::exit_arg_invalid("no argument provided for -d/--output-directory")
})?;
if let Some(prev_path) = output_dir.take() {
return Err(Self::exit_arg_invalid(&format!(
"--output-directory provided twice: {prev_path:?} and {new_path:?}"
)));
} else if stdout_flag {
return Err(Self::exit_arg_invalid(
"--stdout provided along with output dir",
));
} else if !args.is_empty() || stdin_flag || !positional_zips.is_empty() {
return Err(Self::exit_arg_invalid(
"-d/--output-directory provided after entry specs or inputs",
));
} else {
output_dir = Some(new_path);
}
}
b"--mkdir" => {
if mkdir_flag {
return Err(Self::exit_arg_invalid("--mkdir provided twice"));
} else if stdout_flag {
return Err(Self::exit_arg_invalid(
"--stdout provided along with --mkdir",
));
} else if !args.is_empty() || stdin_flag || !positional_zips.is_empty() {
return Err(Self::exit_arg_invalid(
"--mkdir provided after entry specs or inputs",
));
} else {
mkdir_flag = true;
}
}
b"--stdout" => {
if let Some(output_dir) = output_dir.take() {
return Err(Self::exit_arg_invalid(&format!(
"--stdout provided along with output directory {output_dir:?}"
)));
} else if stdout_flag {
return Err(Self::exit_arg_invalid("--stdout provided twice"));
} else if mkdir_flag {
return Err(Self::exit_arg_invalid(
"--stdout provided along with --mkdir",
));
} else if !args.is_empty() || stdin_flag || !positional_zips.is_empty() {
return Err(Self::exit_arg_invalid(
"--stdout provided after entry specs or inputs",
));
} else {
stdout_flag = true;
}
}

/* Transition to entry specs */
b"-a" => {
args.push(ExtractArg::Glob);
}

/* Transition to input args */
b"--stdin" => {
stdin_flag = true;
break;
}
b"--" => break,
arg_bytes => {
if arg_bytes.starts_with(b"-") {
Expand All @@ -789,12 +895,37 @@ Positional paths:
}

positional_zips.extend(argv.into_iter().map(|arg| arg.into()));
if stdin_flag && !positional_zips.is_empty() {
return Err(Self::exit_arg_invalid(&format!(
"--stdin was provided at the same time as positional args {positional_zips:?}"
)));
}
let input = if stdin_flag {
InputType::StreamingStdin
} else {
InputType::ZipPaths(positional_zips)
};

let output = if stdout_flag {
OutputCollation::ConcatenateStdout
} else {
OutputCollation::Filesystem {
output_dir,
mkdir: mkdir_flag,
}
};

Ok(Self {
collation,
output,
args,
positional_zips,
input,
})
}
}

impl crate::driver::ExecuteCommand for Extract {
fn execute(self, err: impl std::io::Write) -> Result<(), crate::CommandError> {
crate::extract::execute_extract(err, self)
}
}
}
9 changes: 9 additions & 0 deletions cli/src/extract.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use std::io::{self, Write};

use crate::{args::extract::*, CommandError, WrapCommandErr};

pub fn execute_extract(mut err: impl Write, extract: Extract) -> Result<(), CommandError> {
writeln!(err, "asdf!").unwrap();
dbg!(extract);
Ok(())
}
3 changes: 2 additions & 1 deletion cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::{fs, io};

pub mod args;
pub mod compress;
pub mod extract;

pub enum ErrHandle<W> {
Output(W),
Expand Down Expand Up @@ -159,7 +160,7 @@ pub mod driver {

match command {
ZipCommand::Info => todo!("info command not implemented"),
ZipCommand::Extract(_extract) => todo!("extract command not implemented"),
ZipCommand::Extract(extract) => extract.do_main(err),
ZipCommand::Compress(compress) => compress.do_main(err),
}
}
Expand Down

0 comments on commit 3731b9e

Please sign in to comment.