diff --git a/Cargo.lock b/Cargo.lock index b72af88..41829a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ dependencies = [ [[package]] name = "assert_cmd" -version = "2.0.2" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e996dc7940838b7ef1096b882e29ec30a3149a3a443cdc8dba19ed382eca1fe2" +checksum = "93ae1ddd39efd67689deb1979d80bad3bf7f2b09c6e6117c8d1f2443b5e2f83e" dependencies = [ "bstr", "doc-comment", @@ -109,9 +109,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.71" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" dependencies = [ "jobserver", ] @@ -134,9 +134,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "3.0.0-beta.5" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feff3878564edb93745d58cf63e17b63f24142506e7a20c87a5521ed7bfb1d63" +checksum = "12e8611f9ae4e068fa3e56931fded356ff745e70987ff76924a6e0ab1c8ef2e3" dependencies = [ "atty", "bitflags", @@ -147,14 +147,22 @@ dependencies = [ "strsim", "termcolor", "textwrap", - "unicase", +] + +[[package]] +name = "clap_complete" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d044e9db8cd0f68191becdeb5246b7462e4cf0c069b19ae00d1bf3fa9889498d" +dependencies = [ + "clap", ] [[package]] name = "clap_derive" -version = "3.0.0-beta.5" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b15c6b4f786ffb6192ffe65a36855bc1fc2444bcd0945ae16748dcd6ed7d0d3" +checksum = "517358c28fcef6607bf6f76108e02afad7e82297d132a6b846dcc1fc3efcd153" dependencies = [ "heck", "proc-macro-error", @@ -163,15 +171,6 @@ dependencies = [ "syn", ] -[[package]] -name = "clap_generate" -version = "3.0.0-beta.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "097ab5db1c3417442270cd57c8dd39f6c3114d3ce09d595f9efddbb1fcfaa799" -dependencies = [ - "clap", -] - [[package]] name = "console" version = "0.15.0" @@ -187,13 +186,23 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836" dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-utils" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcae03edb34f947e64acdb1c33ec169824e20657e9ecb61cef6c8c74dcb8120" +dependencies = [ + "cfg-if", + "lazy_static", +] + [[package]] name = "difflib" version = "0.4.0" @@ -218,6 +227,15 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "fastrand" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "779d043b6a0b90cc4c0ed7ee380a6504394cee7efd7db050e3774eee387324b2" +dependencies = [ + "instant", +] + [[package]] name = "filetime" version = "0.2.15" @@ -257,15 +275,28 @@ checksum = "5ebd3504ad6116843b8375ad70df74e7bfe83cac77a1f3fe73200c844d43bfe0" [[package]] name = "getrandom" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" dependencies = [ "cfg-if", "libc", "wasi", ] +[[package]] +name = "globset" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + [[package]] name = "hashbrown" version = "0.11.2" @@ -274,12 +305,9 @@ checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" [[package]] name = "heck" -version = "0.3.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" [[package]] name = "hermit-abi" @@ -291,10 +319,28 @@ dependencies = [ ] [[package]] -name = "indexmap" -version = "1.7.0" +name = "ignore" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" +dependencies = [ + "crossbeam-utils", + "globset", + "lazy_static", + "log", + "memchr", + "regex", + "same-file", + "thread_local", + "walkdir", + "winapi-util", +] + +[[package]] +name = "indexmap" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" dependencies = [ "autocfg", "hashbrown", @@ -322,10 +368,19 @@ dependencies = [ ] [[package]] -name = "itertools" -version = "0.10.1" +name = "instant" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" dependencies = [ "either", ] @@ -347,9 +402,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.106" +version = "0.2.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a60553f9a9e039a333b4e9b20573b9e9b9c0bb3a11e201ccc48ef4283456d673" +checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" [[package]] name = "libz-sys" @@ -368,6 +423,15 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + [[package]] name = "lzma-sys" version = "0.1.17" @@ -421,15 +485,15 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "once_cell" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" [[package]] name = "os_str_bytes" -version = "4.2.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addaa943333a514159c80c97ff4a93306530d965d27e139188283cd13e06a799" +checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" dependencies = [ "memchr", ] @@ -442,9 +506,10 @@ dependencies = [ "atty", "bzip2", "clap", - "clap_generate", + "clap_complete", "flate2", "fs-err", + "ignore", "indicatif", "infer", "libc", @@ -458,7 +523,6 @@ dependencies = [ "tar", "tempfile", "test-strategy", - "walkdir", "xz2", "zip", "zstd", @@ -492,21 +556,21 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.22" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f" +checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" [[package]] name = "ppv-lite86" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] name = "predicates" -version = "2.0.3" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6ce811d0b2e103743eec01db1c50612221f173084ce2f7941053e94b6bb474" +checksum = "a5aab5be6e4732b473071984b3164dbbfb7a3674d30ea5ff44410b6bcd960c3c" dependencies = [ "difflib", "itertools", @@ -515,15 +579,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57e35a3326b75e49aa85f5dc6ec15b41108cf5aee58eabb1f274dd18b73c2451" +checksum = "da1c2388b1513e1b605fcec39a95e0a9e8ef088f71443ef37099fa9ae6673fcb" [[package]] name = "predicates-tree" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "338c7be2905b732ae3984a2f40032b5e94fd8f52505b186c7d4d68d193445df7" +checksum = "4d86de6de25020a36c6d3643a86d9a6a9f552107c0559c60ea03551b5e16c032" dependencies = [ "predicates-core", "termtree", @@ -555,9 +619,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.32" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" dependencies = [ "unicode-xid", ] @@ -596,9 +660,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quote" -version = "1.0.10" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d" dependencies = [ "proc-macro2", ] @@ -612,7 +676,6 @@ dependencies = [ "libc", "rand_chacha", "rand_core", - "rand_hc", ] [[package]] @@ -634,15 +697,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core", -] - [[package]] name = "rand_xorshift" version = "0.3.0" @@ -751,9 +805,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.81" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" +checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7" dependencies = [ "proc-macro2", "quote", @@ -762,9 +816,9 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f5515d3add52e0bbdcad7b83c388bb36ba7b754dda3b5f5bc2d38640cdba5c" +checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" dependencies = [ "filetime", "libc", @@ -773,13 +827,13 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" dependencies = [ "cfg-if", + "fastrand", "libc", - "rand", "redox_syscall", "remove_dir_all", "winapi", @@ -806,9 +860,9 @@ dependencies = [ [[package]] name = "termtree" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13a4ec180a2de59b57434704ccfad967f789b12737738798fa08798cd5824c16" +checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" [[package]] name = "test-strategy" @@ -827,9 +881,6 @@ name = "textwrap" version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" -dependencies = [ - "unicode-width", -] [[package]] name = "thiserror" @@ -852,26 +903,14 @@ dependencies = [ ] [[package]] -name = "unicase" -version = "2.6.0" +name = "thread_local" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" dependencies = [ - "version_check", + "once_cell", ] -[[package]] -name = "unicode-segmentation" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" - -[[package]] -name = "unicode-width" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" - [[package]] name = "unicode-xid" version = "0.2.2" @@ -892,9 +931,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wait-timeout" @@ -985,18 +1024,18 @@ dependencies = [ [[package]] name = "zstd" -version = "0.9.0+zstd.1.5.0" +version = "0.9.2+zstd.1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07749a5dc2cb6b36661290245e350f15ec3bbb304e493db54a1d354480522ccd" +checksum = "2390ea1bf6c038c39674f22d95f0564725fc06034a47129179810b2fc58caa54" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "4.1.1+zstd.1.5.0" +version = "4.1.3+zstd.1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91c90f2c593b003603e5e0493c837088df4469da25aafff8bce42ba48caf079" +checksum = "e99d81b99fb3c2c2c794e3fe56c305c63d5173a16a46b5850b07c935ffc7db79" dependencies = [ "libc", "zstd-sys", @@ -1004,9 +1043,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "1.6.1+zstd.1.5.0" +version = "1.6.2+zstd.1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "615120c7a2431d16cf1cf979e7fc31ba7a5b5e5707b29c8a99e5dbf8a8392a33" +checksum = "2daf2f248d9ea44454bfcb2516534e8b8ad2fc91bf818a1885495fc42bc8ac9f" dependencies = [ "cc", "libc", diff --git a/Cargo.toml b/Cargo.toml index 47f764a..2e088e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ description = "A command-line utility for easily compressing and decompressing f [dependencies] atty = "0.2.14" bzip2 = "0.4.3" -clap = "=3.0.0-beta.5" # Keep it pinned while in beta! +clap = { version = "3.0.4", features = ["derive", "env"] } flate2 = { version = "1.0.22", default-features = false } fs-err = "2.6.0" libc = "0.2.103" @@ -24,16 +24,16 @@ lzzzz = "0.8.0" once_cell = "1.8.0" snap = "1.0.5" tar = "0.4.37" -walkdir = "2.3.2" xz2 = "0.1.6" zip = { version = "0.5.13", default-features = false } zstd = { version = "0.9.0", default-features = false } tempfile = "3.2.0" +ignore = "0.4.18" indicatif = "0.16.2" [build-dependencies] -clap = "=3.0.0-beta.5" -clap_generate = "=3.0.0-beta.5" +clap = { version = "3.0.4", features = ["derive", "env"] } +clap_complete = "3.0.2" [dev-dependencies] assert_cmd = "2.0.2" diff --git a/LICENSE b/LICENSE index bd024e7..3491346 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 Vinícius R. Miguel +Copyright (c) 2021-2022 Vinícius R. Miguel, João Marcos P. Bezerra and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/build.rs b/build.rs index 1c1a0e9..a8b0677 100644 --- a/build.rs +++ b/build.rs @@ -1,7 +1,7 @@ use std::{env, fs::create_dir_all, path::Path}; use clap::{ArgEnum, IntoApp}; -use clap_generate::{generate_to, Shell}; +use clap_complete::{generate_to, Shell}; include!("src/opts.rs"); diff --git a/src/archive/tar.rs b/src/archive/tar.rs index 330c950..acf0908 100644 --- a/src/archive/tar.rs +++ b/src/archive/tar.rs @@ -10,13 +10,12 @@ use std::{ use fs_err as fs; use tar; -use walkdir::WalkDir; use crate::{ error::FinalError, info, list::FileInArchive, - utils::{self, Bytes}, + utils::{self, Bytes, FileVisibilityPolicy}, }; /// Unpacks the archive given by `archive` into the folder given by `into`. @@ -79,7 +78,12 @@ pub fn list_archive( } /// Compresses the archives given by `input_filenames` into the file given previously to `writer`. -pub fn build_archive_from_paths(input_filenames: &[PathBuf], writer: W, mut display_handle: D) -> crate::Result +pub fn build_archive_from_paths( + input_filenames: &[PathBuf], + writer: W, + file_visibility_policy: FileVisibilityPolicy, + mut display_handle: D, +) -> crate::Result where W: Write, D: Write, @@ -92,7 +96,7 @@ where // Safe unwrap, input shall be treated before let filename = filename.file_name().unwrap(); - for entry in WalkDir::new(&filename) { + for entry in file_visibility_policy.build_walker(filename) { let entry = entry?; let path = entry.path(); diff --git a/src/archive/zip.rs b/src/archive/zip.rs index b661358..147bc51 100644 --- a/src/archive/zip.rs +++ b/src/archive/zip.rs @@ -9,14 +9,16 @@ use std::{ }; use fs_err as fs; -use walkdir::WalkDir; use zip::{self, read::ZipFile, ZipArchive}; use crate::{ error::FinalError, info, list::FileInArchive, - utils::{self, cd_into_same_dir_as, concatenate_os_str_list, get_invalid_utf8_paths, strip_cur_dir, to_utf, Bytes}, + utils::{ + self, cd_into_same_dir_as, concatenate_os_str_list, get_invalid_utf8_paths, strip_cur_dir, to_utf, Bytes, + FileVisibilityPolicy, + }, }; /// Unpacks the archive given by `archive` into the folder given by `output_folder`. @@ -119,7 +121,12 @@ where } /// Compresses the archives given by `input_filenames` into the file given previously to `writer`. -pub fn build_archive_from_paths(input_filenames: &[PathBuf], writer: W, mut display_handle: D) -> crate::Result +pub fn build_archive_from_paths( + input_filenames: &[PathBuf], + writer: W, + file_visibility_policy: FileVisibilityPolicy, + mut display_handle: D, +) -> crate::Result where W: Write + Seek, D: Write, @@ -144,7 +151,7 @@ where // Safe unwrap, input shall be treated before let filename = filename.file_name().unwrap(); - for entry in WalkDir::new(filename) { + for entry in file_visibility_policy.build_walker(filename) { let entry = entry?; let path = entry.path(); diff --git a/src/cli.rs b/src/cli.rs index 2988181..f8578e6 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -10,7 +10,7 @@ use clap::Parser; use fs_err as fs; use once_cell::sync::OnceCell; -use crate::{Opts, QuestionPolicy, Subcommand}; +use crate::{utils::FileVisibilityPolicy, Opts, QuestionPolicy, Subcommand}; /// Whether to enable accessible output (removes info output and reduces other /// output, removes visual markers like '[' and ']'). @@ -23,7 +23,7 @@ impl Opts { /// And: /// 1. Make paths absolute. /// 2. Checks the QuestionPolicy. - pub fn parse_args() -> crate::Result<(Self, QuestionPolicy)> { + pub fn parse_args() -> crate::Result<(Self, QuestionPolicy, FileVisibilityPolicy)> { let mut opts = Self::parse(); ACCESSIBLE.set(opts.accessible).unwrap(); @@ -40,7 +40,14 @@ impl Opts { (true, true) => unreachable!(), }; - Ok((opts, skip_questions_positively)) + // TODO: change this to be just a single function call? + let file_visibility_policy = FileVisibilityPolicy::new() + .read_git_exclude(opts.gitignore) + .read_ignore(opts.gitignore) + .read_git_ignore(opts.gitignore) + .read_hidden(opts.hidden); + + Ok((opts, skip_questions_positively, file_visibility_policy)) } } diff --git a/src/commands.rs b/src/commands.rs index b6f1ebb..1264efd 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -24,7 +24,7 @@ use crate::{ progress::Progress, utils::{ self, concatenate_os_str_list, dir_is_empty, nice_directory_display, to_utf, try_infer_extension, - user_wants_to_continue, + user_wants_to_continue, FileVisibilityPolicy, }, warning, Opts, QuestionAction, QuestionPolicy, Subcommand, }; @@ -43,7 +43,11 @@ fn represents_several_files(files: &[PathBuf]) -> bool { } /// Entrypoint of ouch, receives cli options and matches Subcommand to decide what to do -pub fn run(args: Opts, question_policy: QuestionPolicy) -> crate::Result<()> { +pub fn run( + args: Opts, + question_policy: QuestionPolicy, + file_visibility_policy: FileVisibilityPolicy, +) -> crate::Result<()> { match args.cmd { Subcommand::Compress { mut files, output: output_path } => { // If the output_path file exists and is the same as some of the input files, warn the user and skip those inputs (in order to avoid compression recursion) @@ -156,7 +160,8 @@ pub fn run(args: Opts, question_policy: QuestionPolicy) -> crate::Result<()> { formats = new_formats; } } - let compress_result = compress_files(files, formats, output_file, &output_path, question_policy); + let compress_result = + compress_files(files, formats, output_file, &output_path, question_policy, file_visibility_policy); if let Ok(true) = compress_result { // this is only printed once, so it doesn't result in much text. On the other hand, @@ -287,6 +292,7 @@ fn compress_files( output_file: fs::File, output_dir: &Path, question_policy: QuestionPolicy, + file_visibility_policy: FileVisibilityPolicy, ) -> crate::Result { // The next lines are for displaying the progress bar // If the input files contain a directory, then the total size will be underestimated @@ -350,6 +356,7 @@ fn compress_files( archive::tar::build_archive_from_paths( &files, &mut writer, + file_visibility_policy, progress.as_mut().map(Progress::display_handle).unwrap_or(&mut io::stdout()), )?; writer.flush()?; @@ -389,6 +396,7 @@ fn compress_files( archive::zip::build_archive_from_paths( &files, &mut vec_buffer, + file_visibility_policy, progress.as_mut().map(Progress::display_handle).unwrap_or(&mut io::stdout()), )?; let vec_buffer = vec_buffer.into_inner(); diff --git a/src/error.rs b/src/error.rs index b8a7ddb..34a9c99 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,7 +2,10 @@ //! //! All usage errors will pass throught the Error enum, a lot of them in the Error::Custom. -use std::fmt::{self, Display}; +use std::{ + borrow::Cow, + fmt::{self, Display}, +}; use crate::utils::colors::*; @@ -35,15 +38,18 @@ pub enum Error { /// Alias to std's Result with ouch's Error pub type Result = std::result::Result; +/// A string either heap-allocated or located in static storage +pub type CowStr = Cow<'static, str>; + /// Pretty final error message for end users, crashing the program after display. #[derive(Clone, Debug, Default, PartialEq)] pub struct FinalError { /// Should be made of just one line, appears after the "\[ERROR\]" part - title: String, + title: CowStr, /// Shown as a unnumbered list in yellow - details: Vec, + details: Vec, /// Shown as green at the end to give hints on how to work around this error, if it's fixable - hints: Vec, + hints: Vec, } impl Display for FinalError { @@ -86,19 +92,22 @@ impl Display for FinalError { impl FinalError { /// Only constructor - pub fn with_title(title: impl ToString) -> Self { - Self { title: title.to_string(), details: vec![], hints: vec![] } + #[must_use] + pub fn with_title(title: impl Into) -> Self { + Self { title: title.into(), details: vec![], hints: vec![] } } /// Add one detail line, can have multiple - pub fn detail(mut self, detail: impl ToString) -> Self { - self.details.push(detail.to_string()); + #[must_use] + pub fn detail(mut self, detail: impl Into) -> Self { + self.details.push(detail.into()); self } /// Add one hint line, can have multiple - pub fn hint(mut self, hint: impl ToString) -> Self { - self.hints.push(hint.to_string()); + #[must_use] + pub fn hint(mut self, hint: impl Into) -> Self { + self.hints.push(hint.into()); self } } @@ -106,19 +115,23 @@ impl FinalError { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let err = match self { - Error::WalkdirError { reason } => FinalError::with_title(reason), - Error::NotFound { error_title } => FinalError::with_title(error_title).detail("File not found"), + Error::WalkdirError { reason } => FinalError::with_title(reason.to_string()), + Error::NotFound { error_title } => FinalError::with_title(error_title.to_string()).detail("File not found"), Error::CompressingRootFolder => { FinalError::with_title("It seems you're trying to compress the root folder.") .detail("This is unadvisable since ouch does compressions in-memory.") .hint("Use a more appropriate tool for this, such as rsync.") } - Error::IoError { reason } => FinalError::with_title(reason), - Error::Lz4Error { reason } => FinalError::with_title(reason), - Error::AlreadyExists { error_title } => FinalError::with_title(error_title).detail("File already exists"), - Error::InvalidZipArchive(reason) => FinalError::with_title("Invalid zip archive").detail(reason), - Error::PermissionDenied { error_title } => FinalError::with_title(error_title).detail("Permission denied"), - Error::UnsupportedZipArchive(reason) => FinalError::with_title("Unsupported zip archive").detail(reason), + Error::IoError { reason } => FinalError::with_title(reason.to_string()), + Error::Lz4Error { reason } => FinalError::with_title(reason.to_string()), + Error::AlreadyExists { error_title } => { + FinalError::with_title(error_title.to_string()).detail("File already exists") + } + Error::InvalidZipArchive(reason) => FinalError::with_title("Invalid zip archive").detail(*reason), + Error::PermissionDenied { error_title } => { + FinalError::with_title(error_title.to_string()).detail("Permission denied") + } + Error::UnsupportedZipArchive(reason) => FinalError::with_title("Unsupported zip archive").detail(*reason), Error::Custom { reason } => reason.clone(), }; @@ -159,8 +172,8 @@ impl From for Error { } } -impl From for Error { - fn from(err: walkdir::Error) -> Self { +impl From for Error { + fn from(err: ignore::Error) -> Self { Self::WalkdirError { reason: err.to_string() } } } diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 76a8460..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,29 +0,0 @@ -//! This library is just meant to supply needs for the `ouch` binary crate. - -#![warn(missing_docs)] -// Bare URLs in doc comments are not a problem since this project is primarily -// used as a binary. Since `clap` doesn't remove URL markup in it's help output, -// we don't mark them as URLs. This suppresses the relevant rustdoc warning: -#![allow(rustdoc::bare_urls)] - -// Macros should be declared before -pub mod macros; - -pub mod archive; -pub mod cli; -pub mod commands; -pub mod error; -pub mod extension; -pub mod list; -pub mod progress; -pub mod utils; - -/// CLI argparsing definitions, using `clap`. -pub mod opts; - -pub use error::{Error, Result}; -pub use opts::{Opts, Subcommand}; -pub use utils::{QuestionAction, QuestionPolicy}; - -/// The status code returned from `ouch` on error -pub const EXIT_FAILURE: i32 = libc::EXIT_FAILURE; diff --git a/src/main.rs b/src/main.rs index a8b2d3f..deea33c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,33 @@ -use ouch::{commands, Opts, Result}; +// Macros should be declared first +pub mod macros; + +pub mod archive; +pub mod cli; +pub mod commands; +pub mod error; +pub mod extension; +pub mod list; +pub mod progress; +pub mod utils; + +/// CLI argparsing definitions, using `clap`. +pub mod opts; + +use error::{Error, Result}; +use opts::{Opts, Subcommand}; +use utils::{QuestionAction, QuestionPolicy}; + +/// The status code returned from `ouch` on error +pub const EXIT_FAILURE: i32 = libc::EXIT_FAILURE; fn main() { if let Err(err) = run() { eprintln!("{}", err); - std::process::exit(ouch::EXIT_FAILURE); + std::process::exit(EXIT_FAILURE); } } fn run() -> Result<()> { - let (args, skip_questions_positively) = Opts::parse_args()?; - commands::run(args, skip_questions_positively) + let (args, skip_questions_positively, file_visibility_policy) = Opts::parse_args()?; + commands::run(args, skip_questions_positively, file_visibility_policy) } diff --git a/src/opts.rs b/src/opts.rs index ad08251..e33fbdc 100644 --- a/src/opts.rs +++ b/src/opts.rs @@ -9,20 +9,28 @@ use clap::{Parser, ValueHint}; /// /// Repository: https://github.com/ouch-org/ouch #[derive(Parser, Debug)] -#[clap(version)] +#[clap(about, version)] pub struct Opts { /// Skip [Y/n] questions positively. - #[clap(short, long, conflicts_with = "no")] + #[clap(short, long, conflicts_with = "no", global = true)] pub yes: bool, /// Skip [Y/n] questions negatively. - #[clap(short, long)] + #[clap(short, long, global = true)] pub no: bool, /// Activate accessibility mode, reducing visual noise - #[clap(short = 'A', long, env = "ACCESSIBLE")] + #[clap(short = 'A', long, env = "ACCESSIBLE", global = true)] pub accessible: bool, + /// Ignores hidden files + #[clap(short = 'H', long)] + pub hidden: bool, + + /// Ignores files matched by git's ignore files + #[clap(short = 'g', long)] + pub gitignore: bool, + /// Ouch and claps subcommands #[clap(subcommand)] pub cmd: Subcommand, diff --git a/src/utils/file_visibility.rs b/src/utils/file_visibility.rs new file mode 100644 index 0000000..f0570bc --- /dev/null +++ b/src/utils/file_visibility.rs @@ -0,0 +1,68 @@ +use std::path::Path; + +/// Determines which files should be read or ignored during directory walking +pub struct FileVisibilityPolicy { + /// Enables reading .ignore files. + /// + /// Disabled by default. + pub read_ignore: bool, + + /// If enabled, ignores hidden files. + /// + /// Disabled by default + pub read_hidden: bool, + + /// Enables reading .gitignore files. + /// + /// This is enabled by default. + pub read_git_ignore: bool, + + /// Enables reading `.git/info/exclude` files. + pub read_git_exclude: bool, +} + +impl Default for FileVisibilityPolicy { + fn default() -> Self { + Self { read_ignore: false, read_hidden: true, read_git_ignore: false, read_git_exclude: false } + } +} + +impl FileVisibilityPolicy { + pub fn new() -> Self { + Self::default() + } + + #[must_use] + /// Enables reading .ignore files. + pub fn read_ignore(self, read_ignore: bool) -> Self { + Self { read_ignore, ..self } + } + + #[must_use] + /// Enables reading .gitignore files. + pub fn read_git_ignore(self, read_git_ignore: bool) -> Self { + Self { read_git_ignore, ..self } + } + + #[must_use] + /// Enables reading `.git/info/exclude` files. + pub fn read_git_exclude(self, read_git_exclude: bool) -> Self { + Self { read_git_exclude, ..self } + } + + #[must_use] + /// Enables reading `.git/info/exclude` files. + pub fn read_hidden(self, read_hidden: bool) -> Self { + Self { read_hidden, ..self } + } + + /// Walks through a directory using [`ignore::Walk`] + pub fn build_walker(&self, path: impl AsRef) -> ignore::Walk { + ignore::WalkBuilder::new(path) + .git_exclude(self.read_git_exclude) + .git_ignore(self.read_git_ignore) + .ignore(self.read_ignore) + .hidden(self.read_hidden) + .build() + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index e3dde09..78fb2a9 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -4,10 +4,12 @@ //! stdin interaction helpers. pub mod colors; +mod file_visibility; mod formatting; mod fs; mod question; +pub use file_visibility::FileVisibilityPolicy; pub use formatting::{concatenate_os_str_list, nice_directory_display, strip_cur_dir, to_utf, Bytes}; pub use fs::{ cd_into_same_dir_as, clear_path, create_dir_if_non_existent, dir_is_empty, is_symlink, try_infer_extension, diff --git a/tests/integration.rs b/tests/integration.rs index 0519c60..6112825 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -6,7 +6,7 @@ use std::{iter::once, path::PathBuf}; use fs_err as fs; use parse_display::Display; use proptest::sample::size_range; -use rand::{rngs::SmallRng, RngCore, SeedableRng}; +use rand::{rngs::SmallRng, Rng, SeedableRng}; use tempfile::tempdir; use test_strategy::{proptest, Arbitrary}; @@ -18,11 +18,13 @@ use crate::utils::{assert_same_directory, write_random_content}; enum DirectoryExtension { Tar, Tbz, + Tbz2, Tgz, Tlz4, Tlzma, Tsz, Txz, + Tzst, Zip, } @@ -37,6 +39,7 @@ enum FileExtension { Lzma, Sz, Xz, + Zst, } #[derive(Arbitrary, Debug, Display)] @@ -60,12 +63,12 @@ fn create_random_files(dir: impl Into, depth: u8, rng: &mut SmallRng) { let dir = &dir.into(); // create 0 to 7 random files - for _ in 0..rng.next_u32() % 8 { + for _ in 0..rng.gen_range(0..8u32) { write_random_content(&mut tempfile::Builder::new().tempfile_in(dir).unwrap().keep().unwrap().0, rng); } // create more random files in 0 to 3 new directories - for _ in 0..rng.next_u32() % 4 { + for _ in 0..rng.gen_range(0..4u32) { create_random_files(&tempfile::tempdir_in(dir).unwrap().into_path(), depth - 1, rng); } } @@ -81,8 +84,8 @@ fn single_empty_file(ext: Extension, #[any(size_range(0..8).lift())] exts: Vec