mirror of
https://github.com/ouch-org/ouch.git
synced 2025-06-07 12:05:46 +00:00
Merge pull request #16 from vrmiguel/nightly
Update master from nightly
This commit is contained in:
commit
d3de94dfca
32
.github/workflows/build.yml
vendored
32
.github/workflows/build.yml
vendored
@ -17,37 +17,33 @@ jobs:
|
|||||||
- name: Install toolchain
|
- name: Install toolchain
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ matrix.rust }}
|
toolchain: stable
|
||||||
|
target: x86_64-unknown-linux-musl
|
||||||
override: true
|
override: true
|
||||||
|
|
||||||
|
- name: Install dependencies for musl libc
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install musl-tools
|
||||||
- name: Run cargo build
|
- name: Run cargo build
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: build
|
command: build
|
||||||
args: --release
|
args: --release --target x86_64-unknown-linux-musl
|
||||||
|
|
||||||
- name: Run cargo test
|
- name: Run cargo test
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: test
|
command: test
|
||||||
|
|
||||||
- name: Install dependencies for Python test script
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install libmagic1
|
|
||||||
python3 -m pip install python-magic
|
|
||||||
|
|
||||||
- name: Run test script
|
|
||||||
run: python3 makeshift_testing.py
|
|
||||||
|
|
||||||
- name: Strip binary
|
- name: Strip binary
|
||||||
run: strip target/release/ouch
|
run: strip target/x86_64-unknown-linux-musl/release/ouch
|
||||||
|
|
||||||
- name: Upload binary
|
- name: Upload binary
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: 'ouch-ubuntu-18.04-glibc'
|
name: 'ouch-linux-musl'
|
||||||
path: target/release/ouch
|
path: target/x86_64-unknown-linux-musl/release/ouch
|
||||||
|
|
||||||
macos:
|
macos:
|
||||||
name: macOS
|
name: macOS
|
||||||
@ -77,11 +73,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
command: test
|
command: test
|
||||||
|
|
||||||
- name: Install dependencies for Python test script
|
|
||||||
run: |
|
|
||||||
brew install libmagic
|
|
||||||
python3 -m pip install python-magic
|
|
||||||
|
|
||||||
- name: Strip binary
|
- name: Strip binary
|
||||||
run: strip target/release/ouch
|
run: strip target/release/ouch
|
||||||
|
|
||||||
@ -91,9 +82,6 @@ jobs:
|
|||||||
name: 'ouch-macOS'
|
name: 'ouch-macOS'
|
||||||
path: target/release/ouch
|
path: target/release/ouch
|
||||||
|
|
||||||
- name: Run test script
|
|
||||||
run: python3 makeshift_testing.py
|
|
||||||
|
|
||||||
windows:
|
windows:
|
||||||
name: Windows Server
|
name: Windows Server
|
||||||
runs-on: windows-2019
|
runs-on: windows-2019
|
||||||
|
@ -12,10 +12,6 @@
|
|||||||
- [Installation](#Installation)
|
- [Installation](#Installation)
|
||||||
- [Supported operating systems](#Supported-operating-systems)
|
- [Supported operating systems](#Supported-operating-systems)
|
||||||
|
|
||||||
**Note**
|
|
||||||
* This README represents the new, but not yet implemented, interface that `ouch` will use.
|
|
||||||
* For current usage instructions, check [the old README](https://github.com/vrmiguel/ouch/blob/0f453e9dfc70066056b9cc40e8032dcc6ee703bc/README.md).
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Decompressing files
|
### Decompressing files
|
||||||
|
@ -1,89 +0,0 @@
|
|||||||
#!/usr/bin/python3
|
|
||||||
"""
|
|
||||||
Little integration testing script while proper integration tests in Rust aren't implemented.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import magic, os, hashlib
|
|
||||||
|
|
||||||
def make_random_file():
|
|
||||||
with open('test-file', 'wb') as fout:
|
|
||||||
fout.write(os.urandom(2048))
|
|
||||||
|
|
||||||
def sanity_check_format(format: str):
|
|
||||||
make_random_file()
|
|
||||||
md5sum = hashlib.md5(open('test-file', 'rb').read()).hexdigest()
|
|
||||||
os.system(f"cargo run -- -i test-file -o test-file.{format}")
|
|
||||||
os.remove('test-file')
|
|
||||||
os.system(f"cargo run -- -i test-file.{format}")
|
|
||||||
if md5sum != hashlib.md5(open('test-file', 'rb').read()).hexdigest():
|
|
||||||
print("Something went wrong with tar (de)compression.")
|
|
||||||
os._exit(2)
|
|
||||||
os.remove('test-file')
|
|
||||||
os.remove(f'test-file.{format}')
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
|
|
||||||
# We'll use MIME sniffing through magic numbers to
|
|
||||||
# verify if ouch is actually outputting the file formats
|
|
||||||
# that it should
|
|
||||||
|
|
||||||
m = magic.open(magic.MAGIC_MIME)
|
|
||||||
|
|
||||||
try:
|
|
||||||
os.mkdir("testbuilds")
|
|
||||||
except OSError:
|
|
||||||
print ("Could not make testbuilds folder. Exiting.")
|
|
||||||
os._exit(2)
|
|
||||||
|
|
||||||
os.chdir("testbuilds")
|
|
||||||
|
|
||||||
m.load()
|
|
||||||
files = [
|
|
||||||
"src.tar",
|
|
||||||
"src.zip",
|
|
||||||
"src.tar.gz",
|
|
||||||
"src.tar.bz",
|
|
||||||
"src.tar.bz2",
|
|
||||||
"src.tar.lz",
|
|
||||||
"src.tar.lzma",
|
|
||||||
]
|
|
||||||
|
|
||||||
expected_mime_types = [
|
|
||||||
"application/x-tar",
|
|
||||||
"application/zip",
|
|
||||||
"application/gzip",
|
|
||||||
"application/x-bzip2",
|
|
||||||
"application/x-bzip2",
|
|
||||||
"application/x-xz",
|
|
||||||
"application/x-xz"
|
|
||||||
]
|
|
||||||
|
|
||||||
for file in files:
|
|
||||||
rv = os.system(f"cargo run -- -i ../src/ -o {file}")
|
|
||||||
if rv != 0:
|
|
||||||
print(f"Failed while compressing {file}")
|
|
||||||
|
|
||||||
for (file, expected_mime) in zip(files, expected_mime_types):
|
|
||||||
if m.file(file) != expected_mime:
|
|
||||||
print(f"Test failed at file {file}")
|
|
||||||
os._exit(2)
|
|
||||||
|
|
||||||
for (idx, file) in enumerate(files):
|
|
||||||
rv = os.system(f"cargo run -- -i {file} -o out{idx}/")
|
|
||||||
if rv != 0:
|
|
||||||
print(f"Failed while decompressing {file}")
|
|
||||||
os._exit(2)
|
|
||||||
|
|
||||||
os.chdir("..")
|
|
||||||
os.system("rm -rf testbuilds")
|
|
||||||
|
|
||||||
# We'll now verify if ouch is not altering the data it is compressing
|
|
||||||
# and decompressing
|
|
||||||
|
|
||||||
sanity_check_format("tar")
|
|
||||||
sanity_check_format("tar.gz")
|
|
||||||
sanity_check_format("tar.bz")
|
|
||||||
sanity_check_format("tar.bz2")
|
|
||||||
sanity_check_format("tar.lz")
|
|
||||||
sanity_check_format("tar.lzma")
|
|
@ -91,7 +91,7 @@ impl FlagType {
|
|||||||
#[cfg(target_family = "windows")]
|
#[cfg(target_family = "windows")]
|
||||||
{
|
{
|
||||||
use std::os::windows::ffi::OsStrExt;
|
use std::os::windows::ffi::OsStrExt;
|
||||||
iter = text.encode_wide
|
iter = text.encode_wide();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 45 is the code for a hyphen
|
// 45 is the code for a hyphen
|
||||||
|
@ -126,7 +126,7 @@ pub fn filter_flags(
|
|||||||
|
|
||||||
// For each letter in the short arg, except the last one
|
// For each letter in the short arg, except the last one
|
||||||
for (i, letter) in letters.iter().copied().enumerate() {
|
for (i, letter) in letters.iter().copied().enumerate() {
|
||||||
// Safety: this loop only runs when len >= 1
|
// Safety: this loop only runs when len >= 1, so this subtraction is safe
|
||||||
let is_last_letter = i == letters.len() - 1;
|
let is_last_letter = i == letters.len() - 1;
|
||||||
|
|
||||||
let flag_info = short_flags_info.get(&letter).unwrap_or_else(|| {
|
let flag_info = short_flags_info.get(&letter).unwrap_or_else(|| {
|
||||||
@ -149,16 +149,15 @@ pub fn filter_flags(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// pop the next one
|
// pop the next one
|
||||||
let flag_argument = iter.next();
|
let flag_argument = iter.next().unwrap_or_else(|| {
|
||||||
flag_argument.unwrap_or_else(|| {
|
|
||||||
panic!(
|
panic!(
|
||||||
"USer errror: argument flag `argument_flag` came at last, but it \
|
"USer errror: argument flag `argument_flag` came at last, but it \
|
||||||
requires an argument"
|
requires an argument"
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Otherwise, insert it (TODO: grab next one and add it)
|
// Otherwise, insert it.
|
||||||
// result_flags.argument_flags.insert(flag_info.long);
|
result_flags.argument_flags.insert(flag_name, flag_argument);
|
||||||
} else {
|
} else {
|
||||||
// If it was already inserted
|
// If it was already inserted
|
||||||
if result_flags.boolean_flags.contains(flag_name) {
|
if result_flags.boolean_flags.contains(flag_name) {
|
||||||
|
@ -5,6 +5,7 @@ pub fn trim_double_hyphen(flag_text: &str) -> &str {
|
|||||||
chars.as_str()
|
chars.as_str()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Currently unused
|
||||||
/// Util function to skip the single leading short flag hyphen.
|
/// Util function to skip the single leading short flag hyphen.
|
||||||
pub fn trim_single_hyphen(flag_text: &str) -> &str {
|
pub fn trim_single_hyphen(flag_text: &str) -> &str {
|
||||||
let mut chars = flag_text.chars();
|
let mut chars = flag_text.chars();
|
||||||
|
105
src/cli.rs
105
src/cli.rs
@ -2,6 +2,10 @@ use std::{env, ffi::OsString, io, path::PathBuf, vec::Vec};
|
|||||||
|
|
||||||
use oof::{arg_flag, flag};
|
use oof::{arg_flag, flag};
|
||||||
|
|
||||||
|
use crate::debug;
|
||||||
|
|
||||||
|
pub const VERSION: &str = "0.1.5";
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
/// Files to be compressed
|
/// Files to be compressed
|
||||||
@ -66,8 +70,9 @@ pub fn parse_args_from(mut args: Vec<OsString>) -> crate::Result<ParsedArgs> {
|
|||||||
let mut files: Vec<PathBuf> = args.into_iter().map(PathBuf::from).collect();
|
let mut files: Vec<PathBuf> = args.into_iter().map(PathBuf::from).collect();
|
||||||
|
|
||||||
if files.len() < 2 {
|
if files.len() < 2 {
|
||||||
panic!("The compress subcommands demands at least 2 arguments, see usage:.......");
|
return Err(crate::Error::MissingArgumentsForCompression);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safety: we checked that args.len() >= 2
|
// Safety: we checked that args.len() >= 2
|
||||||
let compressed_output_path = files.pop().unwrap();
|
let compressed_output_path = files.pop().unwrap();
|
||||||
|
|
||||||
@ -82,16 +87,18 @@ pub fn parse_args_from(mut args: Vec<OsString>) -> crate::Result<ParsedArgs> {
|
|||||||
// Defaults to decompression when there is no subcommand
|
// Defaults to decompression when there is no subcommand
|
||||||
None => {
|
None => {
|
||||||
flags_info.push(arg_flag!('o', "output"));
|
flags_info.push(arg_flag!('o', "output"));
|
||||||
|
debug!(&flags_info);
|
||||||
|
|
||||||
// Parse flags
|
// Parse flags
|
||||||
let (args, mut flags) = oof::filter_flags(args, &flags_info)?;
|
let (args, mut flags) = oof::filter_flags(args, &flags_info)?;
|
||||||
|
debug!((&args, &flags));
|
||||||
|
|
||||||
let files: Vec<_> = args.into_iter().map(PathBuf::from).collect();
|
let files: Vec<_> = args.into_iter().map(PathBuf::from).collect();
|
||||||
|
// TODO: This line doesn't seem to be working correctly
|
||||||
let output_folder = flags.take_arg("output").map(PathBuf::from);
|
let output_folder = flags.take_arg("output").map(PathBuf::from);
|
||||||
|
|
||||||
// Is the output here fully correct?
|
// Is the output here fully correct?
|
||||||
// With the paths not canonicalized?
|
// With the paths not canonicalized?
|
||||||
|
|
||||||
let command = Command::Decompress {
|
let command = Command::Decompress {
|
||||||
files,
|
files,
|
||||||
output_folder,
|
output_folder,
|
||||||
@ -103,97 +110,3 @@ pub fn parse_args_from(mut args: Vec<OsString>) -> crate::Result<ParsedArgs> {
|
|||||||
|
|
||||||
Ok(parsed_args)
|
Ok(parsed_args)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
fn gen_args(text: &str) -> Vec<OsString> {
|
|
||||||
let args = text.split_whitespace();
|
|
||||||
args.map(OsString::from).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
// // util for test the argument parsing
|
|
||||||
// macro_rules! test {
|
|
||||||
// ($expected_command:expr, $input_text:expr) => {{
|
|
||||||
// assert_eq!(
|
|
||||||
// $expected_command,
|
|
||||||
// oof::try_arg_parsing($input_text.split_whitespace())
|
|
||||||
// )
|
|
||||||
// }};
|
|
||||||
// }
|
|
||||||
|
|
||||||
macro_rules! parse {
|
|
||||||
($input_text:expr) => {{
|
|
||||||
let args = gen_args($input_text);
|
|
||||||
parse_args_from(args).unwrap()
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
// The absolute flags that ignore all the other argparsing rules are --help and --version
|
|
||||||
fn test_absolute_flags() {
|
|
||||||
let expected = Command::ShowHelp;
|
|
||||||
assert_eq!(expected, parse!("").command);
|
|
||||||
assert_eq!(expected, parse!("-h").command);
|
|
||||||
assert_eq!(expected, parse!("--help").command);
|
|
||||||
assert_eq!(expected, parse!("aaaaaaaa --help -o -e aaa").command);
|
|
||||||
assert_eq!(expected, parse!("aaaaaaaa -h").command);
|
|
||||||
assert_eq!(expected, parse!("--help compress aaaaaaaa").command);
|
|
||||||
assert_eq!(expected, parse!("compress --help").command);
|
|
||||||
assert_eq!(expected, parse!("--version --help").command);
|
|
||||||
assert_eq!(expected, parse!("aaaaaaaa -v aaaa -h").command);
|
|
||||||
|
|
||||||
let expected = Command::ShowVersion;
|
|
||||||
assert_eq!(expected, parse!("ouch --version").command);
|
|
||||||
assert_eq!(expected, parse!("ouch a --version b").command);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_arg_parsing_compress_subcommand() {
|
|
||||||
let files = ["a", "b", "c"].iter().map(PathBuf::from).collect();
|
|
||||||
|
|
||||||
let expected = Command::Compress {
|
|
||||||
files,
|
|
||||||
compressed_output_path: "d".into(),
|
|
||||||
};
|
|
||||||
assert_eq!(expected, parse!("compress a b c d").command);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_arg_parsing_decompress_subcommand() {
|
|
||||||
let files: Vec<_> = ["a", "b", "c"].iter().map(PathBuf::from).collect();
|
|
||||||
|
|
||||||
let expected = Command::Decompress {
|
|
||||||
files: files.clone(),
|
|
||||||
output_folder: None,
|
|
||||||
};
|
|
||||||
assert_eq!(expected, parse!("a b c").command);
|
|
||||||
|
|
||||||
let expected = Command::Decompress {
|
|
||||||
files,
|
|
||||||
output_folder: Some("folder".into()),
|
|
||||||
};
|
|
||||||
assert_eq!(expected, parse!("a b c --output folder").command);
|
|
||||||
assert_eq!(expected, parse!("a b --output folder c").command);
|
|
||||||
assert_eq!(expected, parse!("a --output folder b c").command);
|
|
||||||
assert_eq!(expected, parse!("--output folder a b c").command);
|
|
||||||
}
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_arg_parsing_decompress_subcommand() {
|
|
||||||
// let files: Vec<PathBuf> = ["a", "b", "c"].iter().map(PathBuf::from).collect();
|
|
||||||
|
|
||||||
// let expected = Ok(Command::Decompress {
|
|
||||||
// files: files.clone(),
|
|
||||||
// });
|
|
||||||
// test!(expected, "ouch a b c");
|
|
||||||
|
|
||||||
// let files: Vec<PathBuf> = ["a", "b", "c", "d"].iter().map(PathBuf::from).collect();
|
|
||||||
|
|
||||||
// let expected = Ok(Command::Decompress {
|
|
||||||
// files: files.clone(),
|
|
||||||
// });
|
|
||||||
// test!(expected, "ouch a b c d");
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
@ -29,6 +29,7 @@ impl TarCompressor {
|
|||||||
for entry in WalkDir::new(&filename) {
|
for entry in WalkDir::new(&filename) {
|
||||||
let entry = entry?;
|
let entry = entry?;
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
|
println!("Compressing {:?}", path);
|
||||||
if path.is_dir() {
|
if path.is_dir() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,9 @@ impl ZipCompressor {
|
|||||||
if entry_path.is_dir() {
|
if entry_path.is_dir() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.start_file(entry_path.to_string_lossy(), options)?;
|
writer.start_file(entry_path.to_string_lossy(), options)?;
|
||||||
|
println!("Compressing {:?}", entry_path);
|
||||||
let file_bytes = std::fs::read(entry.path())?;
|
let file_bytes = std::fs::read(entry.path())?;
|
||||||
writer.write_all(&*file_bytes)?;
|
writer.write_all(&*file_bytes)?;
|
||||||
}
|
}
|
||||||
|
23
src/error.rs
23
src/error.rs
@ -15,22 +15,14 @@ pub enum Error {
|
|||||||
InvalidZipArchive(&'static str),
|
InvalidZipArchive(&'static str),
|
||||||
PermissionDenied,
|
PermissionDenied,
|
||||||
UnsupportedZipArchive(&'static str),
|
UnsupportedZipArchive(&'static str),
|
||||||
// InputsMustBeDecompressible(PathBuf),
|
|
||||||
InternalError,
|
InternalError,
|
||||||
CompressingRootFolder,
|
CompressingRootFolder,
|
||||||
|
MissingArgumentsForCompression,
|
||||||
WalkdirError,
|
WalkdirError,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
// impl std::error::Error for Error {
|
|
||||||
// fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
||||||
// // TODO: get rid of PartialEq and Eq in self::Error in order to
|
|
||||||
// // correctly use `source`.
|
|
||||||
// None
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
impl fmt::Debug for Error {
|
impl fmt::Debug for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{}", self)
|
write!(f, "{}", self)
|
||||||
@ -42,19 +34,18 @@ impl fmt::Display for Error {
|
|||||||
match self {
|
match self {
|
||||||
Error::MissingExtensionError(filename) => {
|
Error::MissingExtensionError(filename) => {
|
||||||
write!(f, "{} ", "[ERROR]".red())?;
|
write!(f, "{} ", "[ERROR]".red())?;
|
||||||
|
// TODO: show MIME type of the unsupported file
|
||||||
write!(f, "cannot compress to \'{}\', likely because it has an unsupported (or missing) extension.", filename)
|
write!(f, "cannot compress to \'{}\', likely because it has an unsupported (or missing) extension.", filename)
|
||||||
}
|
}
|
||||||
// Error::InputsMustBeDecompressible(file) => {
|
|
||||||
// write!(f, "{} ", "[ERROR]".red())?;
|
|
||||||
// write!(f, "file '{:?}' is not decompressible", file)
|
|
||||||
// }
|
|
||||||
Error::WalkdirError => {
|
Error::WalkdirError => {
|
||||||
// Already printed in the From block
|
// Already printed in the From block
|
||||||
write!(f, "")
|
write!(f, "")
|
||||||
}
|
}
|
||||||
Error::FileNotFound(file) => {
|
Error::FileNotFound(file) => {
|
||||||
write!(f, "{} ", "[ERROR]".red())?;
|
write!(f, "{} ", "[ERROR]".red())?;
|
||||||
// TODO: check if file == ""
|
if file == &PathBuf::from("") {
|
||||||
|
return write!(f, "file not found!");
|
||||||
|
}
|
||||||
write!(f, "file {:?} not found!", file)
|
write!(f, "file {:?} not found!", file)
|
||||||
}
|
}
|
||||||
Error::CompressingRootFolder => {
|
Error::CompressingRootFolder => {
|
||||||
@ -73,6 +64,10 @@ impl fmt::Display for Error {
|
|||||||
"rsync".green()
|
"rsync".green()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Error::MissingArgumentsForCompression => {
|
||||||
|
write!(f, "{} ", "[ERROR]".red())?;
|
||||||
|
write!(f,"The compress subcommands demands at least 2 arguments, see usage: <TODO-USAGE>")
|
||||||
|
}
|
||||||
Error::InternalError => {
|
Error::InternalError => {
|
||||||
write!(f, "{} ", "[ERROR]".red())?;
|
write!(f, "{} ", "[ERROR]".red())?;
|
||||||
write!(f, "You've reached an internal error! This really should not have happened.\nPlease file an issue at {}", "https://github.com/vrmiguel/ouch".green())
|
write!(f, "You've reached an internal error! This really should not have happened.\nPlease file an issue at {}", "https://github.com/vrmiguel/ouch".green())
|
||||||
|
@ -7,9 +7,9 @@ use std::{
|
|||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
cli::Command,
|
cli::{VERSION, Command},
|
||||||
compressors::{
|
compressors::{
|
||||||
BzipCompressor, Compressor, Entry, GzipCompressor, LzmaCompressor, TarCompressor,
|
Entry, Compressor, BzipCompressor, GzipCompressor, LzmaCompressor, TarCompressor,
|
||||||
ZipCompressor,
|
ZipCompressor,
|
||||||
},
|
},
|
||||||
decompressors::{
|
decompressors::{
|
||||||
@ -20,6 +20,7 @@ use crate::{
|
|||||||
extension::{CompressionFormat, Extension},
|
extension::{CompressionFormat, Extension},
|
||||||
file::File,
|
file::File,
|
||||||
utils,
|
utils,
|
||||||
|
debug
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Evaluator {}
|
pub struct Evaluator {}
|
||||||
@ -31,6 +32,7 @@ impl Evaluator {
|
|||||||
pub fn get_compressor(
|
pub fn get_compressor(
|
||||||
file: &File,
|
file: &File,
|
||||||
) -> crate::Result<(Option<BoxedCompressor>, BoxedCompressor)> {
|
) -> crate::Result<(Option<BoxedCompressor>, BoxedCompressor)> {
|
||||||
|
|
||||||
let extension = match &file.extension {
|
let extension = match &file.extension {
|
||||||
Some(extension) => extension.clone(),
|
Some(extension) => extension.clone(),
|
||||||
None => {
|
None => {
|
||||||
@ -212,7 +214,7 @@ impl Evaluator {
|
|||||||
output: Option<&Path>,
|
output: Option<&Path>,
|
||||||
flags: &oof::Flags,
|
flags: &oof::Flags,
|
||||||
) -> crate::Result<()> {
|
) -> crate::Result<()> {
|
||||||
let file = File::from(file_path)?;
|
let file = debug!(File::from(file_path)?);
|
||||||
let output = match output {
|
let output = match output {
|
||||||
Some(inner) => Some(File::from(inner)?),
|
Some(inner) => Some(File::from(inner)?),
|
||||||
None => None,
|
None => None,
|
||||||
@ -266,9 +268,25 @@ impl Evaluator {
|
|||||||
Self::decompress_file(file, output_folder, flags)?;
|
Self::decompress_file(file, output_folder, flags)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Command::ShowHelp => todo!("call help function"),
|
Command::ShowHelp => help_message(),
|
||||||
Command::ShowVersion => todo!("call version function"),
|
Command::ShowVersion => version_message(),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn version_message() {
|
||||||
|
println!("ouch {}", VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn help_message() {
|
||||||
|
version_message();
|
||||||
|
println!("Vinícius R. M. & João M. Bezerra");
|
||||||
|
println!("ouch is a unified compression & decompression utility");
|
||||||
|
println!();
|
||||||
|
println!(" COMPRESSION USAGE:");
|
||||||
|
println!(" ouch compress <input...> output-file");
|
||||||
|
println!("DECOMPRESSION USAGE:");
|
||||||
|
println!(" ouch <input> [-o/--output output-folder]");
|
||||||
|
}
|
@ -7,7 +7,7 @@ use std::{
|
|||||||
|
|
||||||
use CompressionFormat::*;
|
use CompressionFormat::*;
|
||||||
|
|
||||||
use crate::utils::to_utf;
|
use crate::{debug, utils::to_utf};
|
||||||
|
|
||||||
/// Represents the extension of a file, but only really caring about
|
/// Represents the extension of a file, but only really caring about
|
||||||
/// compression formats (and .tar).
|
/// compression formats (and .tar).
|
||||||
|
@ -19,13 +19,12 @@ pub struct File<'a> {
|
|||||||
|
|
||||||
impl<'a> File<'a> {
|
impl<'a> File<'a> {
|
||||||
pub fn from(path: &'a Path) -> crate::Result<Self> {
|
pub fn from(path: &'a Path) -> crate::Result<Self> {
|
||||||
let extension = Extension::from(path.as_ref())?;
|
let extension = Extension::from(path.as_ref()).ok();
|
||||||
eprintln!("dev warning: Should we really ignore the errors from the convertion above?");
|
|
||||||
|
|
||||||
Ok(File {
|
Ok(File {
|
||||||
path,
|
path,
|
||||||
contents_in_memory: None,
|
contents_in_memory: None,
|
||||||
extension: Some(extension),
|
extension
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,6 @@ fn main() {
|
|||||||
|
|
||||||
fn run() -> crate::Result<()> {
|
fn run() -> crate::Result<()> {
|
||||||
let ParsedArgs { command, flags } = cli::parse_args()?;
|
let ParsedArgs { command, flags } = cli::parse_args()?;
|
||||||
dbg!(&command);
|
debug!(&command);
|
||||||
Evaluator::evaluate(command, &flags)
|
Evaluator::evaluate(command, &flags)
|
||||||
}
|
}
|
||||||
|
129
src/test.rs
129
src/test.rs
@ -1,39 +1,112 @@
|
|||||||
// TODO: remove tests of CompressionFormat::try_from since that's no longer used anywhere
|
use std::{fs, path::Path};
|
||||||
|
|
||||||
// use std::{
|
#[allow(dead_code)]
|
||||||
// convert::TryFrom,
|
// ouch's command-line logic uses fs::canonicalize on its inputs so we cannot
|
||||||
// ffi::{OsStr, OsString},
|
// use made-up files for testing.
|
||||||
// fs,
|
// make_dummy_file therefores creates a small temporary file to bypass fs::canonicalize errors
|
||||||
// path::Path,
|
fn make_dummy_file<'a, P>(path: P) -> crate::Result<()>
|
||||||
// };
|
where
|
||||||
|
P: AsRef<Path> + 'a,
|
||||||
|
{
|
||||||
|
fs::write(path.as_ref(), &[2, 3, 4, 5, 6, 7, 8, 9, 10])?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// use crate::{
|
#[allow(dead_code)]
|
||||||
// cli::Command,
|
fn make_dummy_files<'a, P>(paths: &[P]) -> crate::Result<()>
|
||||||
// extension::{CompressionFormat, CompressionFormat::*, Extension},
|
where
|
||||||
// file::File,
|
P: AsRef<Path> + 'a,
|
||||||
// };
|
{
|
||||||
|
let _ = paths
|
||||||
|
.iter()
|
||||||
|
.map(make_dummy_file)
|
||||||
|
.map(Result::unwrap)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// // Helper
|
#[cfg(test)]
|
||||||
// fn gen_args(text: &str) -> Vec<OsString> {
|
mod tests {
|
||||||
// let args = text.split_whitespace();
|
use super::{make_dummy_files};
|
||||||
// args.map(OsString::from).collect()
|
use crate::cli;
|
||||||
// }
|
use crate::cli::Command;
|
||||||
|
use std::{ffi::OsString, fs, path::PathBuf};
|
||||||
|
|
||||||
|
fn gen_args(text: &str) -> Vec<OsString> {
|
||||||
|
let args = text.split_whitespace();
|
||||||
|
args.map(OsString::from).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! parse {
|
||||||
|
($input_text:expr) => {{
|
||||||
|
let args = gen_args($input_text);
|
||||||
|
cli::parse_args_from(args).unwrap()
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// The absolute flags that ignore all the other argparsing rules are --help and --version
|
||||||
|
fn test_absolute_flags() {
|
||||||
|
let expected = Command::ShowHelp;
|
||||||
|
assert_eq!(expected, parse!("").command);
|
||||||
|
assert_eq!(expected, parse!("-h").command);
|
||||||
|
assert_eq!(expected, parse!("--help").command);
|
||||||
|
assert_eq!(expected, parse!("aaaaaaaa --help -o -e aaa").command);
|
||||||
|
assert_eq!(expected, parse!("aaaaaaaa -h").command);
|
||||||
|
assert_eq!(expected, parse!("--help compress aaaaaaaa").command);
|
||||||
|
assert_eq!(expected, parse!("compress --help").command);
|
||||||
|
assert_eq!(expected, parse!("--version --help").command);
|
||||||
|
assert_eq!(expected, parse!("aaaaaaaa -v aaaa -h").command);
|
||||||
|
|
||||||
|
let expected = Command::ShowVersion;
|
||||||
|
assert_eq!(expected, parse!("ouch --version").command);
|
||||||
|
assert_eq!(expected, parse!("ouch a --version b").command);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_arg_parsing_compress_subcommand() -> crate::Result<()> {
|
||||||
|
|
||||||
|
let files = vec!["a", "b", "c"];
|
||||||
|
make_dummy_files(&*files)?;
|
||||||
|
let files= files.iter().map(fs::canonicalize).map(Result::unwrap).collect();
|
||||||
|
|
||||||
|
let expected = Command::Compress {
|
||||||
|
files,
|
||||||
|
compressed_output_path: "d".into(),
|
||||||
|
};
|
||||||
|
assert_eq!(expected, parse!("compress a b c d").command);
|
||||||
|
|
||||||
|
fs::remove_file("a")?;
|
||||||
|
fs::remove_file("b")?;
|
||||||
|
fs::remove_file("c")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_arg_parsing_decompress_subcommand() {
|
||||||
|
let files: Vec<_> = ["a", "b", "c"].iter().map(PathBuf::from).collect();
|
||||||
|
|
||||||
|
let expected = Command::Decompress {
|
||||||
|
files: files.clone(),
|
||||||
|
output_folder: None,
|
||||||
|
};
|
||||||
|
assert_eq!(expected, parse!("a b c").command);
|
||||||
|
|
||||||
|
let expected = Command::Decompress {
|
||||||
|
files,
|
||||||
|
output_folder: Some("folder".into()),
|
||||||
|
};
|
||||||
|
assert_eq!(expected, parse!("a b c --output folder").command);
|
||||||
|
assert_eq!(expected, parse!("a b --output folder c").command);
|
||||||
|
assert_eq!(expected, parse!("a --output folder b c").command);
|
||||||
|
assert_eq!(expected, parse!("--output folder a b c").command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// #[cfg(test)]
|
// #[cfg(test)]
|
||||||
// mod cli {
|
// mod cli {
|
||||||
// use super::*;
|
// use super::*;
|
||||||
|
|
||||||
// // ouch's command-line logic uses fs::canonicalize on its inputs so we cannot
|
|
||||||
// // use made-up files for testing.
|
|
||||||
// // make_dummy_file therefores creates a small temporary file to bypass fs::canonicalize errors
|
|
||||||
// fn make_dummy_file<'a, P>(path: P) -> crate::Result<()>
|
|
||||||
// where
|
|
||||||
// P: AsRef<Path> + 'a,
|
|
||||||
// {
|
|
||||||
// fs::write(path.as_ref(), &[2, 3, 4, 5, 6, 7, 8, 9, 10])?;
|
|
||||||
// Ok(())
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
// #[test]
|
||||||
// fn decompress_files_into_folder() -> crate::Result<()> {
|
// fn decompress_files_into_folder() -> crate::Result<()> {
|
||||||
// make_dummy_file("file.zip")?;
|
// make_dummy_file("file.zip")?;
|
||||||
|
15
src/utils.rs
15
src/utils.rs
@ -9,6 +9,19 @@ use colored::Colorize;
|
|||||||
|
|
||||||
use crate::{dialogs::Confirmation, extension::CompressionFormat, file::File};
|
use crate::{dialogs::Confirmation, extension::CompressionFormat, file::File};
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
macro_rules! debug {
|
||||||
|
($x:expr) => { dbg!($x) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
macro_rules! debug {
|
||||||
|
($x:expr) => { std::convert::identity($x) }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub(crate) fn ensure_exists<'a, P>(path: P) -> crate::Result<()>
|
pub(crate) fn ensure_exists<'a, P>(path: P) -> crate::Result<()>
|
||||||
where
|
where
|
||||||
P: AsRef<Path> + 'a,
|
P: AsRef<Path> + 'a,
|
||||||
@ -69,7 +82,7 @@ pub(crate) fn change_dir_and_return_parent(filename: &Path) -> crate::Result<Pat
|
|||||||
return Err(crate::Error::CompressingRootFolder);
|
return Err(crate::Error::CompressingRootFolder);
|
||||||
};
|
};
|
||||||
|
|
||||||
env::set_current_dir(parent).unwrap();
|
env::set_current_dir(parent).ok().ok_or(crate::Error::CompressingRootFolder)?;
|
||||||
|
|
||||||
Ok(previous_location)
|
Ok(previous_location)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user