diff --git a/cli/src/args.rs b/cli/src/args.rs index 91070ecdf..6eeb80953 100644 --- a/cli/src/args.rs +++ b/cli/src/args.rs @@ -674,6 +674,8 @@ pub mod info { pub mod extract { use super::{ArgParseError, CommandFormat}; + use zip::CompressionMethod; + use std::{collections::VecDeque, ffi::OsString, mem, path::PathBuf}; #[derive(Debug)] @@ -821,11 +823,8 @@ pub mod extract { #[derive(Debug)] pub enum EntryType { - /// file File, - /// dir Dir, - /// symlink Symlink, } @@ -842,13 +841,11 @@ pub mod extract { #[derive(Debug, PartialEq, Eq)] pub enum NonSpecificCompressionMethodArg { - /// any Any, - /// known Known, } - #[derive(Debug, PartialEq, Eq)] + #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum SpecificCompressionMethodArg { Stored, Deflated, @@ -864,6 +861,40 @@ pub mod extract { Xz, } + impl SpecificCompressionMethodArg { + pub const KNOWN_COMPRESSION_METHODS: &[CompressionMethod] = &[ + CompressionMethod::Stored, + CompressionMethod::Deflated, + #[cfg(feature = "deflate64")] + CompressionMethod::Deflate64, + #[cfg(feature = "bzip2")] + CompressionMethod::Bzip2, + #[cfg(feature = "zstd")] + CompressionMethod::Zstd, + #[cfg(feature = "lzma")] + CompressionMethod::Lzma, + #[cfg(feature = "xz")] + CompressionMethod::Xz, + ]; + + pub fn translate_to_zip(self) -> CompressionMethod { + match self { + Self::Stored => CompressionMethod::Stored, + Self::Deflated => CompressionMethod::Deflated, + #[cfg(feature = "deflate64")] + Self::Deflate64 => CompressionMethod::Deflate64, + #[cfg(feature = "bzip2")] + Self::Bzip2 => CompressionMethod::Bzip2, + #[cfg(feature = "zstd")] + Self::Zstd => CompressionMethod::Zstd, + #[cfg(feature = "lzma")] + Self::Lzma => CompressionMethod::Lzma, + #[cfg(feature = "xz")] + Self::Xz => CompressionMethod::Xz, + } + } + } + #[derive(Debug, PartialEq, Eq)] pub enum CompressionMethodArg { NonSpecific(NonSpecificCompressionMethodArg), diff --git a/cli/src/extract.rs b/cli/src/extract.rs index 0b1160182..cb16b315b 100644 --- a/cli/src/extract.rs +++ b/cli/src/extract.rs @@ -180,6 +180,81 @@ where } } +struct Matcher { + err: RefCell, + expr: MatchExpression, +} + +impl Matcher +where + W: Write, +{ + pub fn evaluate(&self, entry: &ZipFile) -> Result { + let Self { err, expr } = self; + Self::recursive_match(err, &expr, entry) + } + + fn recursive_match( + err: &RefCell, + expr: &MatchExpression, + entry: &ZipFile, + ) -> Result { + match expr { + MatchExpression::PrimitivePredicate(predicate) => match predicate { + Predicate::Trivial(trivial) => match trivial { + TrivialPredicate::True => Ok(true), + TrivialPredicate::False => Ok(false), + }, + Predicate::EntryType(entry_type) => match entry_type { + EntryType::File => Ok(!entry.is_dir() && !entry.is_symlink()), + EntryType::Dir => Ok(entry.is_dir()), + EntryType::Symlink => Ok(entry.is_symlink()), + }, + Predicate::CompressionMethod(method_arg) => match method_arg { + CompressionMethodArg::NonSpecific(nonspecific_arg) => match nonspecific_arg { + NonSpecificCompressionMethodArg::Any => Ok(true), + NonSpecificCompressionMethodArg::Known => { + Ok(SpecificCompressionMethodArg::KNOWN_COMPRESSION_METHODS + .contains(&entry.compression())) + } + }, + CompressionMethodArg::Specific(specific_arg) => { + Ok(specific_arg.translate_to_zip() == entry.compression()) + } + }, + Predicate::DepthLimit(limit_arg) => match limit_arg { + DepthLimitArg::Max(max) => { + let max: usize = (*max).into(); + Ok(entry.name().split('/').count() <= max) + } + DepthLimitArg::Min(min) => { + let min: usize = (*min).into(); + Ok(entry.name().split('/').count() >= min) + } + }, + Predicate::Match(match_arg) => todo!("{match_arg:?}"), + }, + MatchExpression::Negated(inner) => { + Self::recursive_match(err, inner.as_ref(), entry).map(|result| !result) + } + MatchExpression::And { + explicit: _, + left, + right, + } => { + /* Short-circuiting, so do left first. */ + Ok(Self::recursive_match(err, left.as_ref(), entry)? + && Self::recursive_match(err, right.as_ref(), entry)?) + } + MatchExpression::Or { left, right } => { + Ok(Self::recursive_match(err, left.as_ref(), entry)? + || Self::recursive_match(err, right.as_ref(), entry)?) + } + MatchExpression::Grouped(inner) => Self::recursive_match(err, inner.as_ref(), entry), + } + } +} + struct ZipFileInput { err: RefCell, inner: ZipArchive,