Merge pull request #294 from Red-Teapot/zip

Implement zip and unzip commands
This commit is contained in:
Sagie Gur-Ari 2023-01-21 16:55:22 +02:00 committed by GitHub
commit 038b0cb6eb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 761 additions and 5 deletions

215
Cargo.lock generated
View file

@ -8,6 +8,18 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aes"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
"opaque-debug",
]
[[package]]
name = "aho-corasick"
version = "0.7.19"
@ -65,6 +77,12 @@ version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5"
[[package]]
name = "base64ct"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf"
[[package]]
name = "bitflags"
version = "1.3.2"
@ -96,17 +114,47 @@ version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
[[package]]
name = "bzip2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0"
dependencies = [
"bzip2-sys",
"libc",
]
[[package]]
name = "bzip2-sys"
version = "0.1.11+1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
dependencies = [
"cc",
"libc",
"pkg-config",
]
[[package]]
name = "cc"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
dependencies = [
"jobserver",
]
[[package]]
name = "cfg-if"
@ -124,11 +172,20 @@ dependencies = [
"js-sys",
"num-integer",
"num-traits",
"time",
"time 0.1.44",
"wasm-bindgen",
"winapi",
]
[[package]]
name = "cipher"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
dependencies = [
"generic-array",
]
[[package]]
name = "codespan-reporting"
version = "0.11.1"
@ -150,6 +207,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "constant_time_eq"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "core-foundation"
version = "0.9.3"
@ -246,6 +309,7 @@ checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c"
dependencies = [
"block-buffer",
"crypto-common",
"subtle",
]
[[package]]
@ -290,6 +354,7 @@ dependencies = [
"walkdir",
"which",
"whoami",
"zip",
]
[[package]]
@ -497,6 +562,15 @@ dependencies = [
"libc",
]
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest",
]
[[package]]
name = "home"
version = "0.5.4"
@ -594,6 +668,15 @@ dependencies = [
"regex",
]
[[package]]
name = "jobserver"
version = "0.1.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b"
dependencies = [
"libc",
]
[[package]]
name = "js-sys"
version = "0.3.60"
@ -740,6 +823,12 @@ version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
[[package]]
name = "opaque-debug"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "openssl"
version = "0.10.42"
@ -785,6 +874,29 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "password-hash"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700"
dependencies = [
"base64ct",
"rand_core",
"subtle",
]
[[package]]
name = "pbkdf2"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917"
dependencies = [
"digest",
"hmac",
"password-hash",
"sha2",
]
[[package]]
name = "percent-encoding"
version = "2.2.0"
@ -1000,6 +1112,17 @@ dependencies = [
"serde",
]
[[package]]
name = "sha1"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "sha2"
version = "0.10.6"
@ -1017,6 +1140,12 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "subtle"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "suppaftp"
version = "4.6.0"
@ -1065,18 +1194,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.37"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.37"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
dependencies = [
"proc-macro2",
"quote",
@ -1103,6 +1232,33 @@ dependencies = [
"winapi",
]
[[package]]
name = "time"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
dependencies = [
"itoa",
"serde",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
[[package]]
name = "time-macros"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2"
dependencies = [
"time-core",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
@ -1389,3 +1545,52 @@ name = "windows_x86_64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
[[package]]
name = "zip"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "537ce7411d25e54e8ae21a7ce0b15840e7bfcff15b51d697ec3266cc76bdf080"
dependencies = [
"aes",
"byteorder",
"bzip2",
"constant_time_eq",
"crc32fast",
"crossbeam-utils",
"flate2",
"hmac",
"pbkdf2",
"sha1",
"time 0.3.17",
"zstd",
]
[[package]]
name = "zstd"
version = "0.11.2+zstd.1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4"
dependencies = [
"zstd-safe",
]
[[package]]
name = "zstd-safe"
version = "5.0.2+zstd.1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db"
dependencies = [
"libc",
"zstd-sys",
]
[[package]]
name = "zstd-sys"
version = "2.0.4+zstd.1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fa202f2ef00074143e219d15b62ffc317d17cc33909feac471c044087cad7b0"
dependencies = [
"cc",
"libc",
]

View file

@ -47,6 +47,7 @@ suppaftp = "^4.6"
walkdir = "^2"
which = { version = "^4", default-features = false }
whoami = "^1"
zip = "^0.6"
[target.'cfg(not(windows))'.dependencies]
uname = "^0.1"

View file

@ -29,6 +29,7 @@ mod temp_file;
mod touch;
mod write_bytes;
mod write_text;
mod zip;
use crate::utils::pckg;
use duckscript::types::command::Commands;
@ -71,5 +72,7 @@ pub(crate) fn load(commands: &mut Commands, parent: &str) -> Result<(), ScriptEr
commands.set(write_bytes::create(&package))?;
commands.set(write_text::create(&package))?;
zip::load(commands, PACKAGE)?;
Ok(())
}

View file

@ -0,0 +1,17 @@
mod pack;
mod unpack;
use crate::utils::pckg;
use duckscript::types::command::Commands;
use duckscript::types::error::ScriptError;
static PACKAGE: &str = "zip";
pub(crate) fn load(commands: &mut Commands, parent: &str) -> Result<(), ScriptError> {
let package = pckg::concat(parent, PACKAGE);
commands.set(pack::create(&package))?;
commands.set(unpack::create(&package))?;
Ok(())
}

View file

@ -0,0 +1,36 @@
```
zip [--base basedir] [--compression comp] [--append] <zipfile> <files>
```
Packs the provided files into a ZIP archive.
File paths in the archive will be relative to current working directory.
### Parameters
- zipfile - The path to the ZIP archive to be created.
- files - One or more file paths to pack. No globbing is performed. However, array
handles containing the files to pack can be used.
- Optional base directory via `--base <basedir>` --- this directory will be used
as a base for the file paths inside the archive.
- Optional append flag via `--append` --- if set, the files will be added to the
existing archive. If the archive does not exist, it will be created.
- Optional compression mode via `--compression comp` where `comp` is one of
`deflate`, `bzip2`, `none`. If not specified, `deflate` is used by default.
### Return value
true if successful
### Examples
```sh
# create some files for the example
mkdir ./test
touch ./test/foo/bar.txt
touch ./test/baz/qux.png
touch ./test/folder/another/file.doc
# pack the files
zipped = zip ./archive.zip ./test/foo/bar.txt ./test/folder/another/file.doc
```

View file

@ -0,0 +1,272 @@
use crate::utils::{pckg, state};
use duckscript::types::command::{Command, CommandResult, Commands};
use duckscript::types::instruction::Instruction;
use duckscript::types::runtime::StateValue;
use std::collections::HashMap;
use std::fs::{File, OpenOptions};
use std::io;
use std::path::Path;
use zip::write::FileOptions;
use zip::{CompressionMethod, ZipWriter};
#[derive(Clone)]
pub(crate) struct CommandImpl {
package: String,
}
enum LookingFor {
Options,
Base,
Compression,
Files,
}
impl Command for CommandImpl {
fn name(&self) -> String {
pckg::concat(&self.package, "Pack")
}
fn aliases(&self) -> Vec<String> {
vec!["zip".to_string()]
}
fn help(&self) -> String {
include_str!("help.md").to_string()
}
fn clone_and_box(&self) -> Box<dyn Command> {
Box::new((*self).clone())
}
fn requires_context(&self) -> bool {
true
}
fn run_with_context(
&self,
arguments: Vec<String>,
state: &mut HashMap<String, StateValue>,
_variables: &mut HashMap<String, String>,
_output_variable: Option<String>,
_instructions: &Vec<Instruction>,
_commands: &mut Commands,
_line: usize,
) -> CommandResult {
if arguments.len() < 2 {
return CommandResult::Error(
"Paths to the ZIP file and/or files to pack are not provided.".to_string(),
);
}
let CommandOptions {
append,
base,
compression,
zipfile,
file_args,
} = match parse_args(&arguments) {
Ok(options) => options,
Err(err) => return CommandResult::Error(err),
};
let files = match collect_files_from_arrays(&file_args, state) {
Ok(files) => files,
Err(err) => return CommandResult::Error(err),
};
let zipfile = Path::new(zipfile);
let zipfile_dir = match zipfile.parent() {
Some(path) => path,
None => return CommandResult::Error("Couldn't parse ZIP file directory.".to_string()),
};
match std::fs::create_dir_all(zipfile_dir) {
Ok(_) => (),
Err(err) => {
return CommandResult::Error(format!("Couldn't create ZIP file directory: {}", err))
}
};
let zip_file_existed = zipfile.exists();
let zip_file = match OpenOptions::new()
.read(true)
.write(true)
.create(!zip_file_existed)
.truncate(!append)
.open(zipfile)
{
Ok(file) => file,
Err(err) => {
return CommandResult::Error(format!("Couldn't create/open ZIP file: {}", err))
}
};
let mut zip = if append && zip_file_existed {
match ZipWriter::new_append(zip_file) {
Ok(writer) => writer,
Err(err) => {
return CommandResult::Error(format!("Couldn't open ZIP file: {}", err))
}
}
} else {
ZipWriter::new(zip_file)
};
let zip_options = FileOptions::default()
.compression_method(compression)
.unix_permissions(0o755);
for file_to_add_str in files {
let file_to_add_path = Path::new(&file_to_add_str);
let mut file_to_add = match File::open(file_to_add_path) {
Ok(file) => file,
Err(err) => {
return CommandResult::Error(format!(
"File does not exist or can't be opened: {} ({})",
file_to_add_str, err
))
}
};
let file_to_add_path_stripped = file_to_add_path
.strip_prefix("./")
.unwrap_or(file_to_add_path);
let file_to_add_path_stripped = match base {
Some(base) => file_to_add_path_stripped
.strip_prefix(base)
.unwrap_or(file_to_add_path_stripped),
None => file_to_add_path_stripped,
};
let file_to_add_path_str = match file_to_add_path_stripped.to_str() {
Some(str) => str,
None => return CommandResult::Error("Invalid file path".to_string()),
};
match zip.start_file(file_to_add_path_str, zip_options) {
Ok(_) => (),
Err(err) => {
return CommandResult::Error(format!(
"Could not write file to archive: {} ({})",
file_to_add_str, err
))
}
};
match io::copy(&mut file_to_add, &mut zip) {
Ok(_) => (),
Err(err) => {
return CommandResult::Error(format!(
"Could not write file to archive: {} ({})",
file_to_add_str, err
))
}
}
}
match zip.finish() {
Ok(_) => (),
Err(err) => {
return CommandResult::Error(format!("Could not finish the archive: {}", err))
}
};
CommandResult::Continue(Some("true".to_string()))
}
}
struct CommandOptions<'a> {
append: bool,
base: Option<&'a str>,
compression: CompressionMethod,
zipfile: &'a str,
file_args: Vec<&'a str>,
}
fn parse_args<'a>(arguments: &'a [String]) -> Result<CommandOptions<'a>, String> {
let mut looking_for = LookingFor::Options;
let mut append = false;
let mut base = None;
let mut compression = CompressionMethod::Deflated;
let mut zipfile = None;
let mut file_args = Vec::new();
for argument in arguments {
match looking_for {
LookingFor::Options => match argument.as_str() {
"--compression" => looking_for = LookingFor::Compression,
"--base" => looking_for = LookingFor::Base,
"--append" => append = true,
_ => {
zipfile = Some(argument.as_str());
looking_for = LookingFor::Files;
}
},
LookingFor::Compression => {
match argument.as_str() {
"deflate" => compression = CompressionMethod::Deflated,
"bzip2" => compression = CompressionMethod::Bzip2,
"none" => compression = CompressionMethod::Stored,
_ => return Err("Unknown compression method.".to_string()),
}
looking_for = LookingFor::Options;
}
LookingFor::Base => {
let base_str = argument.as_str();
base = Some(base_str.strip_prefix("./").unwrap_or(base_str));
looking_for = LookingFor::Options;
}
LookingFor::Files => file_args.push(argument.as_str()),
}
}
if file_args.is_empty() {
return Err("Input files not provided.".to_string());
}
let zipfile = match zipfile {
Some(filename) => filename,
None => return Err("ZIP file name not provided.".to_string()),
};
Ok(CommandOptions {
append,
base,
compression,
zipfile,
file_args,
})
}
fn collect_files_from_arrays(
files: &[&str],
state: &mut HashMap<String, StateValue>,
) -> Result<Vec<String>, String> {
let mut collected = Vec::new();
for arg in files {
match state::get_handle(state, arg.to_string()) {
Some(value) => match value {
StateValue::List(entries) => {
for entry in entries {
collected.push(state::get_as_string(entry)?);
}
}
arg => collected.push(state::get_as_string(arg)?),
},
None => collected.push(arg.to_string()),
};
}
Ok(collected)
}
pub(crate) fn create(package: &str) -> Box<dyn Command> {
Box::new(CommandImpl {
package: package.to_string(),
})
}

View file

@ -0,0 +1,23 @@
```
unzip <zipfile> [target]
```
Unpacks the ZIP file into the `target` directory, if provided, or
into current working directory otherwise.
### Parameters
- zipfile - The path to the ZIP archive to be created.
- target - The directory to unpack files into. If not provided,
current working directory is used.
### Return value
true if successful (i.e. the zip file exists and there are no existing file conflicts)
### Examples
```sh
# ./stuff directory will be created automatically
unzipped = unzip ./archive.zip ./stuff
```

View file

@ -0,0 +1,81 @@
use std::fs::OpenOptions;
use std::path::Path;
use zip::ZipArchive;
use duckscript::types::command::{Command, CommandResult};
use crate::utils::pckg;
#[derive(Clone)]
pub(crate) struct CommandImpl {
package: String,
}
impl Command for CommandImpl {
fn name(&self) -> String {
pckg::concat(&self.package, "Unpack")
}
fn aliases(&self) -> Vec<String> {
vec!["unzip".to_string()]
}
fn help(&self) -> String {
include_str!("help.md").to_string()
}
fn clone_and_box(&self) -> Box<dyn Command> {
Box::new((*self).clone())
}
fn run(&self, arguments: Vec<String>) -> CommandResult {
if arguments.len() < 2 {
return CommandResult::Error(
"Paths to the ZIP file and/or target directory are not provided.".to_string(),
);
}
let zipfile = Path::new(&arguments[0]);
let target_dir = Path::new(&arguments[1]);
match std::fs::create_dir_all(target_dir) {
Ok(_) => (),
Err(err) => {
return CommandResult::Error(format!("Couldn't create target directory: {}", err))
}
};
let zip_file = match OpenOptions::new().read(true).open(zipfile) {
Ok(file) => file,
Err(err) => return CommandResult::Error(format!("Couldn't open ZIP file: {}", err)),
};
let mut zip = match ZipArchive::new(zip_file) {
Ok(archive) => archive,
Err(err) => return CommandResult::Error(format!("Couldn't open ZIP file: {}", err)),
};
for file in zip.file_names() {
let file_path = target_dir.join(file);
if file_path.exists() && file_path.is_file() {
return CommandResult::Error(format!(
"File already exists: {}",
file_path.to_str().unwrap()
));
}
}
match zip.extract(target_dir) {
Ok(_) => (),
Err(err) => return CommandResult::Error(format!("Couldn't unpack ZIP file: {}", err)),
};
CommandResult::Continue(Some("true".to_string()))
}
}
pub(crate) fn create(package: &str) -> Box<dyn Command> {
Box::new(CommandImpl {
package: package.to_string(),
})
}

View file

@ -0,0 +1,87 @@
base = set ./target/_duckscript_test/fs/zip/pack
fn randfile
length = random_range 10 50
text = random_text ${length}
writefile ${1} ${text}
end
fn test_create_simple
randfile ${base}/foo.txt
randfile ${base}/bar/baz.txt
zipped = zip ${base}/test_simple.zip ${base}/foo.txt ${base}/bar/baz.txt
assert ${zipped}
exists = is_file ${base}/test_simple.zip
assert ${exists}
end
fn test_create_from_array
files = array foo.txt bar.txt baz/qux.txt
files_to_zip = array
for file in ${files}
randfile ${base}/${file}
array_push ${files_to_zip} ${base}/${file}
end
zipped = zip ${base}/test_array.zip ${files_to_zip}
assert ${zipped}
exists = is_file ${base}/test_array.zip
assert ${exists}
release ${files}
release ${files_to_zip}
end
fn test_create_base
randfile ${base}/foo.txt
zipped = zip --base ${base} ${base}/test_base.zip ${base}/foo.txt
assert ${zipped}
exists = is_file ${base}/test_base.zip
assert ${exists}
end
fn test_append
randfile ${base}/foo.txt
randfile ${base}/bar.txt
randfile ${base}/baz/qux.txt
zipped = zip --base ${base} ${base}/test_append.zip ${base}/foo.txt
assert ${zipped}
zipped = zip --append --base ${base} ${base}/test_append.zip ${base}/bar.txt
assert ${zipped}
zipped = zip --append --base ${base} ${base}/test_append.zip ${base}/baz/qux.txt
assert ${zipped}
exists = is_file ${base}/test_append.zip
assert ${exists}
end
fn test_compression_mode
randfile ${base}/foo.txt
randfile ${base}/bar.txt
zipped = zip --compression deflate ${base}/test_deflate.zip ${base}/foo.txt ${base}/bar.txt
assert ${zipped}
exists = is_file ${base}/test_deflate.zip
assert ${exists}
zipped = zip --compression bzip2 ${base}/test_bzip2.zip ${base}/foo.txt ${base}/bar.txt
assert ${zipped}
exists = is_file ${base}/test_bzip2.zip
assert ${exists}
zipped = zip --compression none ${base}/test_none.zip ${base}/foo.txt ${base}/bar.txt
assert ${zipped}
exists = is_file ${base}/test_none.zip
assert ${exists}
end

View file

@ -0,0 +1,31 @@
base = set ./target/_duckscript_test/fs/zip/unpack
fn randfile
length = random_range 10 50
text = random_text ${length}
writefile ${1} ${text}
end
fn test_unpack_simple
files = array foo.txt bar/baz.txt bar/baf.txt qux.txt
for file in ${files}
randfile ${base}/src/${file}
end
zipped = zip --base ${base}/src ${base}/test_unpack.zip ${base}/src/foo.txt ${base}/src/bar/baz.txt ${base}/src/bar/baf.txt ${base}/src/qux.txt
assert ${zipped}
exists = is_file ${base}/test_unpack.zip
assert ${exists}
unzipped = unzip ${base}/test_unpack.zip ${base}/unpacked
assert ${unzipped}
for file in ${files}
src_hash = digest --algo sha512 --file ${base}/src/${file}
dst_hash = digest --algo sha512 --file ${base}/unpacked/${file}
assert_eq ${src_hash} ${dst_hash}
end
release ${files}
end