Support --include globbing

This commit is contained in:
Truman Kilen 2024-08-02 14:35:41 -05:00
parent f277c25b3c
commit 55d419df74
4 changed files with 51 additions and 9 deletions

7
Cargo.lock generated
View file

@ -380,6 +380,12 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.4.1" version = "0.4.1"
@ -704,6 +710,7 @@ dependencies = [
"base64", "base64",
"clap", "clap",
"dir-diff", "dir-diff",
"glob",
"hex", "hex",
"indicatif", "indicatif",
"indoc", "indoc",

View file

@ -34,6 +34,7 @@ rayon = "1.10.0"
sha2 = "0.10.8" sha2 = "0.10.8"
strum = { workspace = true } strum = { workspace = true }
itertools = "0.13.0" itertools = "0.13.0"
glob = "0.3.1"
[dev-dependencies] [dev-dependencies]
assert_cmd = "2.0.14" assert_cmd = "2.0.14"

View file

@ -67,7 +67,7 @@ struct ActionUnpack {
/// Files or directories to include. Can be specified multiple times. If not specified, everything is extracted. /// Files or directories to include. Can be specified multiple times. If not specified, everything is extracted.
#[arg(action = clap::ArgAction::Append, short, long)] #[arg(action = clap::ArgAction::Append, short, long)]
include: Vec<String>, include: Vec<glob::Pattern>,
} }
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@ -339,12 +339,6 @@ fn unpack(aes_key: Option<aes::Aes256>, action: ActionUnpack) -> Result<(), repa
let mount_point = PathBuf::from(pak.mount_point()); let mount_point = PathBuf::from(pak.mount_point());
let prefix = Path::new(&action.strip_prefix); let prefix = Path::new(&action.strip_prefix);
let includes = action
.include
.iter()
.map(|i| prefix.join(Path::new(i)))
.collect::<Vec<_>>();
struct UnpackEntry { struct UnpackEntry {
entry_path: String, entry_path: String,
out_path: PathBuf, out_path: PathBuf,
@ -356,9 +350,29 @@ fn unpack(aes_key: Option<aes::Aes256>, action: ActionUnpack) -> Result<(), repa
.into_iter() .into_iter()
.map(|entry_path| { .map(|entry_path| {
let full_path = mount_point.join(&entry_path); let full_path = mount_point.join(&entry_path);
if !includes.is_empty() && !includes.iter().any(|i| full_path.starts_with(i)) { if !action.include.is_empty() {
if let Ok(stripped) = full_path.strip_prefix(prefix) {
let options = glob::MatchOptions {
case_sensitive: true,
require_literal_separator: true,
require_literal_leading_dot: false,
};
if !action.include.iter().any(|i| {
// check full file path
i.matches_path_with(stripped, options)
// check ancestor directories
|| stripped.ancestors().skip(1).any(|a| {
i.matches_path_with(a, options)
// hack to check ancestor directories with trailing slash
|| i.matches_path_with(&a.join(""), options)
})
}) {
return Ok(None); return Ok(None);
} }
} else {
return Ok(None);
}
}
let out_path = output let out_path = output
.join(full_path.strip_prefix(prefix).map_err(|_| { .join(full_path.strip_prefix(prefix).map_err(|_| {
repak::Error::PrefixMismatch { repak::Error::PrefixMismatch {

View file

@ -148,6 +148,26 @@ fn test_cli_unpack() {
// TODO test unpacking to non-empty directory // TODO test unpacking to non-empty directory
} }
#[test]
fn test_cli_unpack_include() {
let dir = tempfile::tempdir().unwrap();
let assert = Command::cargo_bin("repak")
.unwrap()
.arg("unpack")
.arg(PAK)
.arg("-s")
.arg("../mount")
.arg("-i")
.arg("point/**/*.txt")
.arg("-o")
.arg(dir.path())
.assert();
assert.success().stdout(formatdoc! {r#"
Unpacked 2 files to {} from ../repak/tests/packs/pack_v11.pak
"#, &dir.path().to_string_lossy()});
}
#[test] #[test]
fn test_cli_hashlist() { fn test_cli_hashlist() {
let assert = Command::cargo_bin("repak") let assert = Command::cargo_bin("repak")