Compare commits
10 commits
4a7c0748e0
...
dcc571c6d2
Author | SHA1 | Date | |
---|---|---|---|
![]() |
dcc571c6d2 | ||
![]() |
dd8daa26ed | ||
![]() |
5420684061 | ||
![]() |
84a0f2c049 | ||
![]() |
2b34d9ed01 | ||
![]() |
22faadc34e | ||
![]() |
3bc8b9aa80 | ||
![]() |
e3dddc3dd9 | ||
![]() |
3e9a34af7e | ||
![]() |
44e2741727 |
9 changed files with 723 additions and 481 deletions
409
Cargo.lock
generated
409
Cargo.lock
generated
|
@ -19,63 +19,64 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.12"
|
||||
version = "0.6.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540"
|
||||
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.6"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
|
||||
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.3"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
|
||||
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.0.2"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
|
||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.2"
|
||||
version = "3.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
|
||||
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.4"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
|
||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
|
@ -88,18 +89,19 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.14.0"
|
||||
version = "3.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.83"
|
||||
version = "1.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||
checksum = "9157bbaa6b165880c27a4293a474c91cdcf265cc68cc829bf10be0964a391caf"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -110,32 +112,32 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.31"
|
||||
version = "0.4.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
|
||||
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"windows-targets 0.48.5",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.1"
|
||||
version = "4.5.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da"
|
||||
checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.1"
|
||||
version = "4.5.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb"
|
||||
checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
|
@ -145,18 +147,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "4.5.1"
|
||||
version = "4.5.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "885e4d7d5af40bfb99ae6f9433e292feac98d452dcb3ec3d25dfe7552b77da8c"
|
||||
checksum = "d9647a559c112175f17cf724dc72d3645680a883c58481332779192b0d8e7a01"
|
||||
dependencies = [
|
||||
"clap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete_nushell"
|
||||
version = "4.5.1"
|
||||
version = "4.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80d0e48e026ce7df2040239117d25e4e79714907420c70294a5ce4b6bbe6a7b6"
|
||||
checksum = "315902e790cc6e5ddd20cbd313c1d0d49db77f191e149f96397230fb82a17677"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"clap_complete",
|
||||
|
@ -164,15 +166,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.0"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
|
||||
[[package]]
|
||||
name = "clap_mangen"
|
||||
version = "0.2.20"
|
||||
version = "0.2.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1dd95b5ebb5c1c54581dd6346f3ed6a79a3eef95dd372fc2ac13d535535300e"
|
||||
checksum = "fbae9cbfdc5d4fa8711c09bd7b83f644cb48281ac35bf97af3e47b0675864bdf"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"roff",
|
||||
|
@ -180,9 +182,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
|
@ -193,21 +195,21 @@ dependencies = [
|
|||
"encode_unicode",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"unicode-width",
|
||||
"windows-sys",
|
||||
"unicode-width 0.1.14",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.6"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.12"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
|
||||
checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
@ -233,9 +235,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
|
@ -259,9 +261,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.9.0"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
|
@ -299,9 +301,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.59"
|
||||
version = "0.1.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539"
|
||||
checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
|
@ -322,69 +324,67 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "indicatif"
|
||||
version = "0.17.8"
|
||||
version = "0.17.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3"
|
||||
checksum = "cbf675b85ed934d3c67b5c5469701eec7db22689d0a2139d856e0925fa28b281"
|
||||
dependencies = [
|
||||
"console",
|
||||
"instant",
|
||||
"number_prefix",
|
||||
"portable-atomic",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
"unicode-width 0.2.0",
|
||||
"vt100",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.10"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.28"
|
||||
version = "0.1.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6"
|
||||
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.67"
|
||||
version = "0.3.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1"
|
||||
checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.152"
|
||||
version = "0.2.168"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
|
||||
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "md-5"
|
||||
|
@ -398,9 +398,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.17"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
@ -413,9 +413,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
|||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "package-bootstrap"
|
||||
|
@ -431,39 +431,39 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.30"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
||||
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.6.0"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
|
||||
checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.76"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
|
||||
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.8.0"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
|
||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
|
@ -471,9 +471,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.0"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
|
||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
|
@ -481,9 +481,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "roff"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316"
|
||||
checksum = "88f8660c1ff60292143c98d08fc6e2f654d722db50410e3f3797d40baaf9d8f3"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
|
@ -517,16 +517,22 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.0"
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.48"
|
||||
version = "2.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -550,33 +556,39 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
|||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.11.0"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.11"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "vt100"
|
||||
|
@ -586,7 +598,7 @@ checksum = "84cd863bf0db7e392ba3bd04994be3473491b31e66340672af5d11943c6274de"
|
|||
dependencies = [
|
||||
"itoa",
|
||||
"log",
|
||||
"unicode-width",
|
||||
"unicode-width 0.1.14",
|
||||
"vte",
|
||||
]
|
||||
|
||||
|
@ -603,9 +615,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "vte_generate_state_changes"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff"
|
||||
checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -613,9 +625,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.4.0"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
|
||||
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
|
@ -623,23 +635,23 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.90"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406"
|
||||
checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.90"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd"
|
||||
checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
|
@ -648,9 +660,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.90"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999"
|
||||
checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
|
@ -658,9 +670,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.90"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7"
|
||||
checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -671,48 +683,36 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.90"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b"
|
||||
checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
name = "web-time"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.6"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.0",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -721,146 +721,105 @@ version = "0.52.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.0",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.48.5",
|
||||
"windows_aarch64_msvc 0.48.5",
|
||||
"windows_i686_gnu 0.48.5",
|
||||
"windows_i686_msvc 0.48.5",
|
||||
"windows_x86_64_gnu 0.48.5",
|
||||
"windows_x86_64_gnullvm 0.48.5",
|
||||
"windows_x86_64_msvc 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.0",
|
||||
"windows_aarch64_msvc 0.52.0",
|
||||
"windows_i686_gnu 0.52.0",
|
||||
"windows_i686_msvc 0.52.0",
|
||||
"windows_x86_64_gnu 0.52.0",
|
||||
"windows_x86_64_gnullvm 0.52.0",
|
||||
"windows_x86_64_msvc 0.52.0",
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "0.13.0"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bffb3309596d527cfcba7dfc6ed6052f1d39dfbd7c867aa2e865e4a449c10110"
|
||||
checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9"
|
||||
dependencies = [
|
||||
"zstd-safe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-safe"
|
||||
version = "7.0.0"
|
||||
version = "7.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43747c7422e2924c11144d5229878b98180ef8b06cca4ab5af37afc8a8d8ea3e"
|
||||
checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059"
|
||||
dependencies = [
|
||||
"zstd-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-sys"
|
||||
version = "2.0.9+zstd.1.5.5"
|
||||
version = "2.0.13+zstd.1.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656"
|
||||
checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
|
|
|
@ -153,3 +153,5 @@ to see Haggis implemented in other languages.
|
|||
- [x] Add path to error message when passing between threads
|
||||
- [x] Add ability to write archives to stdout
|
||||
- [x] Add ability to read archives from stdin
|
||||
- [x] Add option to display total size to archive listings
|
||||
- [x] Optionally display sizes in human readable form
|
||||
|
|
|
@ -40,7 +40,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||
let bs = Bootstrap::new("haggis", cli::haggis(), &outdir);
|
||||
bs.install(target_dir, 1)?;
|
||||
if matches.get_flag("meta") {
|
||||
bs.docfiles(&["README.md", "LICENSE.md"], &Path::new("haggis"))?;
|
||||
bs.docfiles(&["README.md", "LICENSE.md"], Path::new("haggis"))?;
|
||||
}
|
||||
Bootstrap::new("haggis-create", cli::create(), &outdir).manpage(1)?;
|
||||
Bootstrap::new("haggis-extract", cli::extract(), &outdir).manpage(1)?;
|
||||
|
|
25
src/cli.rs
25
src/cli.rs
|
@ -27,6 +27,11 @@ pub fn extract() -> Command {
|
|||
.long("quiet")
|
||||
.visible_alias("silent")
|
||||
.action(ArgAction::SetFalse),
|
||||
Arg::new("verbose")
|
||||
.help("List the extracted files")
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("stdin")
|
||||
.help("Read archive from stdin")
|
||||
.short('i')
|
||||
|
@ -53,6 +58,11 @@ pub fn extract() -> Command {
|
|||
.long("gid")
|
||||
.value_parser(clap::value_parser!(u32))
|
||||
.num_args(1),
|
||||
Arg::new("total")
|
||||
.help("Display the total size of extracted files")
|
||||
.short('t')
|
||||
.long("total")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("archive")
|
||||
.num_args(1)
|
||||
.required_unless_present("stdin")
|
||||
|
@ -94,6 +104,11 @@ pub fn create() -> Command {
|
|||
.long("quiet")
|
||||
.visible_alias("silent")
|
||||
.action(ArgAction::SetFalse),
|
||||
Arg::new("verbose")
|
||||
.help("List the archived files")
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("stdout")
|
||||
.help("Write archive to stdout")
|
||||
.short('o')
|
||||
|
@ -154,6 +169,16 @@ pub fn list() -> Command {
|
|||
.short('n')
|
||||
.long("no-sort")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("total")
|
||||
.help("Display the total size if extracted")
|
||||
.short('t')
|
||||
.long("total")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("human")
|
||||
.help("Display sizes in human-readable form")
|
||||
.short('H')
|
||||
.long("human")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("archive")
|
||||
.num_args(1)
|
||||
.required(true)
|
||||
|
|
262
src/haggis.rs
262
src/haggis.rs
|
@ -1,14 +1,20 @@
|
|||
#![warn(clippy::all, clippy::pedantic)]
|
||||
use {
|
||||
clap::ArgMatches,
|
||||
haggis::{Algorithm, Listing, ListingKind, ListingStream, NodeStream, Message, StreamMessage},
|
||||
haggis::{
|
||||
Algorithm, HumanSize, Listing, ListingKind, ListingStream, Message, NodeStream,
|
||||
StreamMessage,
|
||||
},
|
||||
indicatif::{ProgressBar, ProgressStyle},
|
||||
std::{
|
||||
fs::{self, File},
|
||||
io::{self, BufReader, BufWriter},
|
||||
os::fd::{AsRawFd, FromRawFd},
|
||||
process,
|
||||
sync::mpsc,
|
||||
sync::{
|
||||
atomic::{AtomicU64, Ordering},
|
||||
mpsc, Arc,
|
||||
},
|
||||
thread,
|
||||
},
|
||||
walkdir::WalkDir,
|
||||
|
@ -19,6 +25,72 @@ mod cli;
|
|||
|
||||
static TEMPLATE: &str = "[ {prefix:^30!} ] {wide_bar}{pos:>5.cyan}/{len:5.green}";
|
||||
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
struct ListingFlags {
|
||||
files: bool,
|
||||
color: bool,
|
||||
long: bool,
|
||||
human: bool,
|
||||
}
|
||||
|
||||
impl From<&ArgMatches> for ListingFlags {
|
||||
fn from(matches: &ArgMatches) -> Self {
|
||||
Self {
|
||||
files: matches.get_flag("files"),
|
||||
color: matches.get_flag("color"),
|
||||
long: matches.get_flag("long"),
|
||||
human: matches.get_flag("human"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
enum Verbosity {
|
||||
Quiet,
|
||||
Normal,
|
||||
Verbose,
|
||||
}
|
||||
|
||||
impl From<&ArgMatches> for Verbosity {
|
||||
fn from(matches: &ArgMatches) -> Self {
|
||||
if matches.get_flag("quiet") {
|
||||
Self::Quiet
|
||||
} else if matches.get_flag("verbose") && !matches.get_flag("stdout") {
|
||||
Self::Verbose
|
||||
} else {
|
||||
Self::Normal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CreateFlags {
|
||||
algorithm: Algorithm,
|
||||
zstd_compression: Option<i32>,
|
||||
verbosity: Verbosity,
|
||||
stdout: bool,
|
||||
uid: Option<u32>,
|
||||
gid: Option<u32>,
|
||||
}
|
||||
|
||||
impl TryFrom<&ArgMatches> for CreateFlags {
|
||||
type Error = haggis::Error;
|
||||
|
||||
fn try_from(matches: &ArgMatches) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
algorithm: matches.get_one::<String>("algorithm").unwrap().parse()?,
|
||||
zstd_compression: if matches.get_flag("zstd") {
|
||||
Some(matches.get_one::<i32>("level").copied().unwrap_or(3))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
verbosity: Verbosity::from(matches),
|
||||
stdout: matches.get_flag("stdout"),
|
||||
uid: matches.get_one::<u32>("uid").copied(),
|
||||
gid: matches.get_one::<u32>("gid").copied(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let matches = cli::haggis().get_matches();
|
||||
match matches.subcommand() {
|
||||
|
@ -51,10 +123,7 @@ fn main() {
|
|||
|
||||
#[allow(clippy::similar_names)]
|
||||
fn create(matches: &ArgMatches) -> Result<(), haggis::Error> {
|
||||
let verbose = !matches.get_flag("stdout") || matches.get_flag("quiet");
|
||||
let algorithm: Algorithm = matches.get_one::<String>("algorithm").unwrap().parse()?;
|
||||
let uid = matches.get_one::<u32>("uid").copied();
|
||||
let gid = matches.get_one::<u32>("gid").copied();
|
||||
let flags = CreateFlags::try_from(matches)?;
|
||||
let mut files = vec![];
|
||||
if let Some(f) = matches.get_many::<String>("files") {
|
||||
for f in f {
|
||||
|
@ -79,24 +148,30 @@ fn create(matches: &ArgMatches) -> Result<(), haggis::Error> {
|
|||
let (sender, receiver) = mpsc::channel();
|
||||
let len = files.len();
|
||||
let mut handle = None;
|
||||
if verbose {
|
||||
if flags.verbosity == Verbosity::Verbose {
|
||||
let pb = ProgressBar::new(len as u64);
|
||||
pb.set_style(ProgressStyle::with_template(TEMPLATE).unwrap());
|
||||
pb.set_prefix("Adding files");
|
||||
if let Some(o) = output {
|
||||
pb.println(format!("Creating archive {o}"));
|
||||
}
|
||||
let m = matches.clone();
|
||||
handle = Some(thread::spawn(move || {
|
||||
for msg in &receiver {
|
||||
match msg {
|
||||
Message::NodeCreated(s) => {
|
||||
pb.set_prefix(s.split('/').last().unwrap().to_string());
|
||||
pb.inc(1);
|
||||
}
|
||||
Message::NodeSaved { name, size } => {
|
||||
let name = name.split('/').last().unwrap();
|
||||
pb.set_prefix(format!("{name} added, {size} bytes"));
|
||||
Message::NodeSaved(n) => {
|
||||
let name = n.name.split('/').last().unwrap();
|
||||
if let ListingKind::Normal(size) = n.kind {
|
||||
pb.set_prefix(format!("{name} added, {size} bytes"));
|
||||
} else {
|
||||
pb.set_prefix(format!("{name} added"));
|
||||
}
|
||||
pb.inc(1);
|
||||
if m.get_flag("verbose") {
|
||||
pb.suspend(|| {
|
||||
let _res = print_listing(&n, &m);
|
||||
});
|
||||
}
|
||||
}
|
||||
Message::Eof => {
|
||||
pb.finish_and_clear();
|
||||
|
@ -109,34 +184,54 @@ fn create(matches: &ArgMatches) -> Result<(), haggis::Error> {
|
|||
}
|
||||
}));
|
||||
}
|
||||
if matches.get_flag("zstd") {
|
||||
let level = matches.get_one::<i32>("level").copied().unwrap_or(3);
|
||||
if matches.get_flag("stdout") {
|
||||
if let Some(level) = flags.zstd_compression {
|
||||
if flags.stdout {
|
||||
let stdout = io::stdout();
|
||||
let mut writer = Encoder::new(stdout, level)?;
|
||||
haggis::par_stream_archive(&mut writer, &files, algorithm, &sender, uid, gid)?;
|
||||
haggis::par_stream_archive(
|
||||
&mut writer,
|
||||
&files,
|
||||
flags.algorithm,
|
||||
&sender,
|
||||
flags.uid,
|
||||
flags.gid,
|
||||
)?;
|
||||
let _fd = writer.finish();
|
||||
} else if let Some(o) = output {
|
||||
let fd = File::create(o)?;
|
||||
let mut writer = Encoder::new(fd, level)?;
|
||||
haggis::par_stream_archive(&mut writer, &files, algorithm, &sender, uid, gid)?;
|
||||
haggis::par_stream_archive(
|
||||
&mut writer,
|
||||
&files,
|
||||
flags.algorithm,
|
||||
&sender,
|
||||
flags.uid,
|
||||
flags.gid,
|
||||
)?;
|
||||
let _fd = writer.finish()?;
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
} else if matches.get_flag("stdout") {
|
||||
} else if flags.stdout {
|
||||
let stdout = io::stdout();
|
||||
let mut writer = BufWriter::new(stdout);
|
||||
haggis::par_stream_archive(&mut writer, &files, algorithm, &sender, uid, gid)?;
|
||||
haggis::par_stream_archive(
|
||||
&mut writer,
|
||||
&files,
|
||||
flags.algorithm,
|
||||
&sender,
|
||||
flags.uid,
|
||||
flags.gid,
|
||||
)?;
|
||||
} else if let Some(o) = output {
|
||||
haggis::par_create_archive(o, &files, algorithm, &sender, uid, gid)?;
|
||||
haggis::par_create_archive(o, &files, flags.algorithm, &sender, flags.uid, flags.gid)?;
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
if let Some(handle) = handle {
|
||||
match handle.join() {
|
||||
Ok(()) => {
|
||||
if verbose {
|
||||
if flags.verbosity == Verbosity::Verbose {
|
||||
println!("Archive created successfully");
|
||||
}
|
||||
Ok(())
|
||||
|
@ -153,6 +248,7 @@ fn create(matches: &ArgMatches) -> Result<(), haggis::Error> {
|
|||
|
||||
#[allow(clippy::similar_names)]
|
||||
fn extract(matches: &ArgMatches) -> Result<(), haggis::Error> {
|
||||
let total = Arc::new(AtomicU64::new(0));
|
||||
let file = matches.get_one::<String>("archive");
|
||||
let uid = matches.get_one::<u32>("uid").copied();
|
||||
let gid = matches.get_one::<u32>("gid").copied();
|
||||
|
@ -174,12 +270,15 @@ fn extract(matches: &ArgMatches) -> Result<(), haggis::Error> {
|
|||
let dir = matches.get_one::<String>("change");
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
let file = file.cloned().unwrap_or("stdin".to_string());
|
||||
let total_ref = total.clone();
|
||||
let handle = if zst {
|
||||
let reader = Decoder::new(fd)?;
|
||||
let mut stream = NodeStream::new(reader)?;
|
||||
let handle = if matches.get_flag("quiet") {
|
||||
let matches = matches.clone();
|
||||
Some(thread::spawn(move || {
|
||||
progress(&file, &receiver, u64::from(stream.length));
|
||||
let t = progress(&file, &receiver, u64::from(stream.length), &matches);
|
||||
total_ref.store(t, Ordering::Relaxed);
|
||||
Ok::<(), haggis::Error>(())
|
||||
}))
|
||||
} else {
|
||||
|
@ -191,8 +290,10 @@ fn extract(matches: &ArgMatches) -> Result<(), haggis::Error> {
|
|||
let reader = BufReader::new(fd);
|
||||
let mut stream = NodeStream::new(reader)?;
|
||||
let handle = if matches.get_flag("quiet") {
|
||||
let matches = matches.clone();
|
||||
Some(thread::spawn(move || {
|
||||
progress(&file, &receiver, u64::from(stream.length));
|
||||
let t = progress(&file, &receiver, u64::from(stream.length), &matches);
|
||||
total_ref.store(t, Ordering::Relaxed);
|
||||
Ok::<(), haggis::Error>(())
|
||||
}))
|
||||
} else {
|
||||
|
@ -204,7 +305,12 @@ fn extract(matches: &ArgMatches) -> Result<(), haggis::Error> {
|
|||
if let Some(handle) = handle {
|
||||
match handle.join() {
|
||||
Ok(_) => {
|
||||
if matches.get_flag("quiet") {
|
||||
if matches.get_flag("total") {
|
||||
println!(
|
||||
"{} extracted",
|
||||
HumanSize::from(total.load(Ordering::Relaxed))
|
||||
);
|
||||
} else if matches.get_flag("quiet") {
|
||||
println!("Archive extracted successfully");
|
||||
}
|
||||
Ok(())
|
||||
|
@ -219,33 +325,53 @@ fn extract(matches: &ArgMatches) -> Result<(), haggis::Error> {
|
|||
}
|
||||
}
|
||||
|
||||
fn progress(file: &str, receiver: &mpsc::Receiver<StreamMessage>, len: u64) {
|
||||
fn progress(
|
||||
file: &str,
|
||||
receiver: &mpsc::Receiver<StreamMessage>,
|
||||
len: u64,
|
||||
matches: &ArgMatches,
|
||||
) -> u64 {
|
||||
let mut total: u64 = 0;
|
||||
let pb = ProgressBar::new(len);
|
||||
pb.set_style(ProgressStyle::with_template(TEMPLATE).unwrap());
|
||||
pb.set_prefix("Extracting files");
|
||||
pb.println(format!("Extracting archive {file}"));
|
||||
for msg in receiver {
|
||||
match msg {
|
||||
StreamMessage::FileExtracted { name, size } => {
|
||||
let name = name.split('/').last().unwrap();
|
||||
pb.set_prefix(format!("{name} extracted, {size} bytes"));
|
||||
pb.inc(1);
|
||||
}
|
||||
StreamMessage::LinkCreated { name, target } => {
|
||||
let name = name.split('/').last().unwrap();
|
||||
let target = target.split('/').last().unwrap();
|
||||
pb.set_prefix(format!("{name} -> {target}"));
|
||||
pb.inc(1);
|
||||
}
|
||||
StreamMessage::DirectoryCreated { name } => {
|
||||
let name = name.split('/').last().unwrap();
|
||||
pb.set_prefix(format!("mkdir {name}"));
|
||||
pb.inc(1);
|
||||
}
|
||||
StreamMessage::DeviceCreated { name } => {
|
||||
let name = name.split('/').last().unwrap();
|
||||
pb.set_prefix(format!("mknod {name}"));
|
||||
pb.inc(1);
|
||||
StreamMessage::NodeExtracted(n) => {
|
||||
let name = n.name.split('/').last().unwrap();
|
||||
match n.kind {
|
||||
ListingKind::Normal(size) => {
|
||||
pb.set_prefix(format!("{name} extracted, {size} bytes"));
|
||||
pb.inc(1);
|
||||
total += size;
|
||||
}
|
||||
ListingKind::SoftLink(ref t) | ListingKind::HardLink(ref t) => {
|
||||
pb.set_prefix(format!("{name} -> {t}"));
|
||||
pb.inc(1);
|
||||
}
|
||||
ListingKind::Directory => {
|
||||
pb.set_prefix(format!("mkdir {name}"));
|
||||
pb.inc(1);
|
||||
}
|
||||
ListingKind::Block(_) | ListingKind::Character(_) => {
|
||||
pb.set_prefix(format!("mknod {name}"));
|
||||
pb.inc(1);
|
||||
}
|
||||
ListingKind::Fifo => {
|
||||
pb.set_prefix(format!("mkfifo {name}"));
|
||||
pb.inc(1);
|
||||
}
|
||||
ListingKind::Eof => {
|
||||
pb.finish_and_clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if matches.get_flag("verbose") {
|
||||
pb.suspend(|| {
|
||||
let _res = print_listing(&n, matches);
|
||||
});
|
||||
}
|
||||
}
|
||||
StreamMessage::Eof => {
|
||||
pb.finish_and_clear();
|
||||
|
@ -256,20 +382,22 @@ fn progress(file: &str, receiver: &mpsc::Receiver<StreamMessage>, len: u64) {
|
|||
}
|
||||
}
|
||||
}
|
||||
total
|
||||
}
|
||||
|
||||
fn print_listing(li: &Listing, matches: &ArgMatches) -> Result<(), haggis::Error> {
|
||||
if matches.get_flag("files") && li.kind == ListingKind::Directory {
|
||||
let flags = ListingFlags::from(matches);
|
||||
if flags.files && li.kind == ListingKind::Directory {
|
||||
return Ok(());
|
||||
}
|
||||
if matches.get_flag("color") {
|
||||
if matches.get_flag("long") {
|
||||
li.print_color()?;
|
||||
if flags.color {
|
||||
if flags.long {
|
||||
li.print_color(flags.human)?;
|
||||
} else {
|
||||
li.print_color_simple()?;
|
||||
}
|
||||
} else if matches.get_flag("long") {
|
||||
println!("{li}");
|
||||
} else if flags.long {
|
||||
li.print(flags.human);
|
||||
} else {
|
||||
println!("{}", li.name);
|
||||
}
|
||||
|
@ -277,15 +405,22 @@ fn print_listing(li: &Listing, matches: &ArgMatches) -> Result<(), haggis::Error
|
|||
}
|
||||
|
||||
fn list_unsorted(matches: &ArgMatches) -> Result<(), haggis::Error> {
|
||||
let mut total: u64 = 0;
|
||||
let file = matches.get_one::<String>("archive").unwrap();
|
||||
let fd = File::open(file)?;
|
||||
if matches.get_flag("zstd") {
|
||||
let mut fd = File::open(file)?;
|
||||
let zst = matches.get_flag("zstd") || haggis::detect_zstd(&mut fd)?;
|
||||
if zst {
|
||||
let reader = Decoder::new(fd)?;
|
||||
let stream = NodeStream::new(reader)?;
|
||||
for node in stream {
|
||||
let node = node?;
|
||||
let li = Listing::from(node);
|
||||
print_listing(&li, matches)?;
|
||||
if matches.get_flag("total") {
|
||||
if let ListingKind::Normal(s) = li.kind {
|
||||
total += s;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let reader = BufReader::new(fd);
|
||||
|
@ -293,12 +428,21 @@ fn list_unsorted(matches: &ArgMatches) -> Result<(), haggis::Error> {
|
|||
for li in stream {
|
||||
let li = li?;
|
||||
print_listing(&li, matches)?;
|
||||
if matches.get_flag("total") {
|
||||
if let ListingKind::Normal(s) = li.kind {
|
||||
total += s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if matches.get_flag("total") {
|
||||
println!("Total: {}", HumanSize::from(total));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn list(matches: &ArgMatches) -> Result<(), haggis::Error> {
|
||||
let mut total: u64 = 0;
|
||||
let file = matches.get_one::<String>("archive").unwrap();
|
||||
let mut fd = File::open(file)?;
|
||||
let zst = matches.get_flag("zstd") || haggis::detect_zstd(&mut fd)?;
|
||||
|
@ -320,6 +464,14 @@ fn list(matches: &ArgMatches) -> Result<(), haggis::Error> {
|
|||
};
|
||||
for li in list {
|
||||
print_listing(&li, matches)?;
|
||||
if matches.get_flag("total") {
|
||||
if let ListingKind::Normal(s) = li.kind {
|
||||
total += s;
|
||||
}
|
||||
}
|
||||
}
|
||||
if matches.get_flag("total") {
|
||||
println!("Total: {}", HumanSize::from(total));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
40
src/humansize.rs
Normal file
40
src/humansize.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
use std::fmt;
|
||||
|
||||
const KILOS: f64 = 1024.0;
|
||||
const MEGAS: f64 = KILOS * 1024.0;
|
||||
const GIGAS: f64 = MEGAS * 1024.0;
|
||||
const TERAS: f64 = GIGAS * 1024.0;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum HumanSize {
|
||||
Bytes(u64),
|
||||
KiloBytes(f64),
|
||||
MegaBytes(f64),
|
||||
GigaBytes(f64),
|
||||
TeraBytes(f64),
|
||||
}
|
||||
|
||||
impl fmt::Display for HumanSize {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Bytes(b) => write!(f, "{b:>7}"),
|
||||
Self::KiloBytes(k) => write!(f, "{k:>6.1}K"),
|
||||
Self::MegaBytes(m) => write!(f, "{m:>6.1}M"),
|
||||
Self::GigaBytes(g) => write!(f, "{g:>6.1}G"),
|
||||
Self::TeraBytes(t) => write!(f, "{t:>6.1}T"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
impl From<u64> for HumanSize {
|
||||
fn from(value: u64) -> Self {
|
||||
match value {
|
||||
n if n as f64 > TERAS => Self::GigaBytes(n as f64 / TERAS),
|
||||
n if n as f64 > GIGAS => Self::GigaBytes(n as f64 / GIGAS),
|
||||
n if n as f64 > MEGAS => Self::MegaBytes(n as f64 / MEGAS),
|
||||
n if n as f64 > KILOS => Self::KiloBytes(n as f64 / KILOS),
|
||||
_ => Self::Bytes(value),
|
||||
}
|
||||
}
|
||||
}
|
151
src/lib.rs
151
src/lib.rs
|
@ -16,6 +16,7 @@ mod checksum;
|
|||
mod error;
|
||||
mod file;
|
||||
mod filetype;
|
||||
mod humansize;
|
||||
mod listing;
|
||||
mod listing_stream;
|
||||
pub(crate) mod nix;
|
||||
|
@ -28,6 +29,7 @@ pub use {
|
|||
error::Error,
|
||||
file::File,
|
||||
filetype::FileType,
|
||||
humansize::HumanSize,
|
||||
listing::Kind as ListingKind,
|
||||
listing::Listing,
|
||||
listing_stream::ListingStream,
|
||||
|
@ -64,6 +66,130 @@ pub(crate) fn load_string<R: Read>(reader: &mut R) -> Result<String, Error> {
|
|||
Ok(String::from_utf8(buf)?)
|
||||
}
|
||||
|
||||
pub struct Creator<W: Write> {
|
||||
pub writer: W,
|
||||
pub algorithm: Algorithm,
|
||||
pub uid: Option<u32>,
|
||||
pub gid: Option<u32>,
|
||||
}
|
||||
|
||||
impl Creator<BufWriter<std::fs::File>> {
|
||||
pub fn new_for_path(
|
||||
path: &str,
|
||||
algorithm: Algorithm,
|
||||
uid: Option<u32>,
|
||||
gid: Option<u32>,
|
||||
) -> Result<Self, Error> {
|
||||
let fd = fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(path)?;
|
||||
let writer = BufWriter::new(fd);
|
||||
Ok(Self {
|
||||
writer,
|
||||
algorithm,
|
||||
uid,
|
||||
gid,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Creator<BufWriter<std::io::StdoutLock<'_>>> {
|
||||
#[allow(clippy::must_use_candidate)]
|
||||
pub fn new_for_stdout(algorithm: Algorithm, uid: Option<u32>, gid: Option<u32>) -> Self {
|
||||
let stdout = io::stdout().lock();
|
||||
let writer = BufWriter::new(stdout);
|
||||
Self {
|
||||
writer,
|
||||
algorithm,
|
||||
uid,
|
||||
gid,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Creator<W> {
|
||||
/// Creates and streams a haggis archive over something which implements `Write`
|
||||
/// # Errors
|
||||
/// Returns `crate::Error` if io fails or several other error conditions
|
||||
pub fn stream_archive(&mut self, files: &[String]) -> Result<(), Error> {
|
||||
self.writer.write_all(&MAGIC)?;
|
||||
let len = u32::try_from(files.len())?;
|
||||
self.writer.write_all(&len.to_le_bytes())?;
|
||||
let links = Mutex::new(HashMap::new());
|
||||
for f in files {
|
||||
let mut node = Node::from_path(f, self.algorithm, &links)?;
|
||||
if let Some(n) = self.uid {
|
||||
node.uid = n;
|
||||
}
|
||||
if let Some(n) = self.gid {
|
||||
node.gid = n;
|
||||
}
|
||||
node.write(&mut self.writer)?;
|
||||
}
|
||||
self.writer.write_all(&[0; 8])?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parallel")]
|
||||
impl<W: Write + Send> Creator<W> {
|
||||
/// Creates and streams a Haggis archive from a list of files, processing each
|
||||
/// file in parallel
|
||||
/// # Errors
|
||||
/// Returns `crate::Error` if io fails or several other error conditions
|
||||
pub fn par_stream_archive(
|
||||
mut self,
|
||||
files: &[String],
|
||||
sender: &Sender<Message>,
|
||||
) -> Result<(), Error> {
|
||||
self.writer.write_all(&MAGIC)?;
|
||||
let len = u32::try_from(files.len())?;
|
||||
self.writer.write_all(&len.to_le_bytes())?;
|
||||
let links = Mutex::new(HashMap::<u64, String>::new());
|
||||
let writer = Mutex::new(self.writer);
|
||||
let s = sender.clone();
|
||||
files.par_iter().try_for_each_with(s, |s, f| {
|
||||
let mut node = match Node::from_path(f, self.algorithm, &links) {
|
||||
Ok(n) => n,
|
||||
Err(error) => {
|
||||
s.send(Message::Err {
|
||||
name: f.to_string(),
|
||||
error,
|
||||
})
|
||||
.map_err(|_| Error::SenderError)?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
if let Some(n) = self.uid {
|
||||
node.uid = n;
|
||||
}
|
||||
if let Some(n) = self.gid {
|
||||
node.gid = n;
|
||||
}
|
||||
if let Ok(mut writer) = writer.lock() {
|
||||
let mut writer = &mut *writer;
|
||||
if let Err(error) = node.write(&mut writer) {
|
||||
s.send(Message::Err {
|
||||
name: node.name.clone(),
|
||||
error,
|
||||
})
|
||||
.map_err(|_| Error::SenderError)?;
|
||||
}
|
||||
s.send(Message::NodeSaved(node.into()))
|
||||
.map_err(|_| Error::SenderError)?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::MutexError)
|
||||
}
|
||||
})?;
|
||||
let mut writer = writer.into_inner().map_err(|_| Error::MutexError)?;
|
||||
writer.write_all(&[0; 8])?;
|
||||
sender.send(Message::Eof).map_err(|_| Error::SenderError)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::similar_names)]
|
||||
/// Creates a haggis archive from a list of files
|
||||
/// # Errors
|
||||
|
@ -135,18 +261,8 @@ pub fn stream_archive<W: Write>(
|
|||
#[cfg(feature = "parallel")]
|
||||
#[derive(Debug)]
|
||||
pub enum Message {
|
||||
/// A `Node` has successfully been created from this path
|
||||
NodeCreated(
|
||||
/// The path of the created `Node`
|
||||
String,
|
||||
),
|
||||
/// The `Node` has successfully been written into the writer
|
||||
NodeSaved {
|
||||
/// The path of the saved `Node`
|
||||
name: String,
|
||||
/// The size in bytes of the created `Node`
|
||||
size: u64,
|
||||
},
|
||||
NodeSaved(Listing),
|
||||
/// An error occurred creating or writing out the node
|
||||
Err {
|
||||
/// The pathname of the node
|
||||
|
@ -239,17 +355,8 @@ pub fn par_stream_archive<W: Write + Send>(
|
|||
})
|
||||
.map_err(|_| Error::SenderError)?;
|
||||
}
|
||||
match node.filetype {
|
||||
FileType::Normal(n) => s
|
||||
.send(Message::NodeSaved {
|
||||
name: node.name.clone(),
|
||||
size: n.len,
|
||||
})
|
||||
.map_err(|_| Error::SenderError)?,
|
||||
_ => s
|
||||
.send(Message::NodeCreated(node.name.clone()))
|
||||
.map_err(|_| Error::SenderError)?,
|
||||
}
|
||||
s.send(Message::NodeSaved(node.into()))
|
||||
.map_err(|_| Error::SenderError)?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::MutexError)
|
||||
|
|
189
src/listing.rs
189
src/listing.rs
|
@ -5,11 +5,10 @@ use {
|
|||
};
|
||||
|
||||
use {
|
||||
crate::{filetype::Flag, Error, FileType, Node, Special},
|
||||
chrono::NaiveDateTime,
|
||||
crate::{filetype::Flag, Error, FileType, HumanSize, Node, Special},
|
||||
chrono::DateTime,
|
||||
std::{
|
||||
cmp::Ordering,
|
||||
fmt,
|
||||
io::{Read, Seek, SeekFrom},
|
||||
},
|
||||
};
|
||||
|
@ -172,84 +171,6 @@ impl Ord for Listing {
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Listing {
|
||||
#[allow(clippy::cast_possible_wrap)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}{}{}{}{}{}{}{}{}{} {:>4}:{:<4} ",
|
||||
match self.kind {
|
||||
Kind::Normal(_) => "-",
|
||||
Kind::HardLink(_) => "L",
|
||||
Kind::SoftLink(_) => "l",
|
||||
Kind::Directory => "d",
|
||||
Kind::Character(_) => "c",
|
||||
Kind::Block(_) => "b",
|
||||
Kind::Fifo => "p",
|
||||
Kind::Eof => return Ok(()),
|
||||
},
|
||||
match self.mode {
|
||||
m if m & 0o400 != 0 => "r",
|
||||
_ => "-",
|
||||
},
|
||||
match self.mode {
|
||||
m if m & 0o200 != 0 => "w",
|
||||
_ => "-",
|
||||
},
|
||||
match self.mode {
|
||||
m if m & 0o4000 != 0 => "S",
|
||||
m if m & 0o100 != 0 => "x",
|
||||
_ => "-",
|
||||
},
|
||||
match self.mode {
|
||||
m if m & 0o40 != 0 => "r",
|
||||
_ => "-",
|
||||
},
|
||||
match self.mode {
|
||||
m if m & 0o20 != 0 => "w",
|
||||
_ => "-",
|
||||
},
|
||||
match self.mode {
|
||||
m if m & 0o2000 != 0 => "S",
|
||||
m if m & 0o10 != 0 => "x",
|
||||
_ => "-",
|
||||
},
|
||||
match self.mode {
|
||||
m if m & 0o4 != 0 => "r",
|
||||
_ => "-",
|
||||
},
|
||||
match self.mode {
|
||||
m if m & 0o2 != 0 => "w",
|
||||
_ => "-",
|
||||
},
|
||||
match self.mode {
|
||||
m if m & 0o1000 != 0 => "t",
|
||||
m if m & 0o1 != 0 => "x",
|
||||
_ => "-",
|
||||
},
|
||||
self.uid,
|
||||
self.gid,
|
||||
)?;
|
||||
match self.kind {
|
||||
Kind::Normal(s) => write!(f, "{s:>10} "),
|
||||
_ => write!(f, "{:>10}", "-"),
|
||||
}?;
|
||||
match NaiveDateTime::from_timestamp_opt(self.mtime as i64, 0) {
|
||||
Some(dt) => write!(f, "{dt} ")?,
|
||||
_ => write!(f, "{:>19} ", self.mtime)?,
|
||||
}
|
||||
match self.kind {
|
||||
Kind::Directory | Kind::Fifo | Kind::Normal(_) => write!(f, "{}", self.name),
|
||||
Kind::HardLink(ref tgt) => write!(f, "{}=>{}", self.name, tgt),
|
||||
Kind::SoftLink(ref tgt) => write!(f, "{}->{}", self.name, tgt),
|
||||
Kind::Character(ref sp) | Kind::Block(ref sp) => {
|
||||
write!(f, "{} {},{}", self.name, sp.major, sp.minor)
|
||||
}
|
||||
Kind::Eof => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Listing {
|
||||
/// Reads all metadata from a haggis Node without reading the file's actual
|
||||
/// data.
|
||||
|
@ -292,6 +213,92 @@ impl Listing {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn print(&self, human: bool) {
|
||||
print!(
|
||||
"{}{}{}{}{}{}{}{}{}{} {:>4}:{:<4} ",
|
||||
match self.kind {
|
||||
Kind::Normal(_) => "-",
|
||||
Kind::HardLink(_) => "L",
|
||||
Kind::SoftLink(_) => "l",
|
||||
Kind::Directory => "d",
|
||||
Kind::Character(_) => "c",
|
||||
Kind::Block(_) => "b",
|
||||
Kind::Fifo => "p",
|
||||
Kind::Eof => return,
|
||||
},
|
||||
match self.mode {
|
||||
m if m & 0o400 != 0 => "r",
|
||||
_ => "-",
|
||||
},
|
||||
match self.mode {
|
||||
m if m & 0o200 != 0 => "w",
|
||||
_ => "-",
|
||||
},
|
||||
match self.mode {
|
||||
m if m & 0o4000 != 0 => "S",
|
||||
m if m & 0o100 != 0 => "x",
|
||||
_ => "-",
|
||||
},
|
||||
match self.mode {
|
||||
m if m & 0o40 != 0 => "r",
|
||||
_ => "-",
|
||||
},
|
||||
match self.mode {
|
||||
m if m & 0o20 != 0 => "w",
|
||||
_ => "-",
|
||||
},
|
||||
match self.mode {
|
||||
m if m & 0o2000 != 0 => "S",
|
||||
m if m & 0o10 != 0 => "x",
|
||||
_ => "-",
|
||||
},
|
||||
match self.mode {
|
||||
m if m & 0o4 != 0 => "r",
|
||||
_ => "-",
|
||||
},
|
||||
match self.mode {
|
||||
m if m & 0o2 != 0 => "w",
|
||||
_ => "-",
|
||||
},
|
||||
match self.mode {
|
||||
m if m & 0o1000 != 0 => "t",
|
||||
m if m & 0o1 != 0 => "x",
|
||||
_ => "-",
|
||||
},
|
||||
self.uid,
|
||||
self.gid,
|
||||
);
|
||||
match self.kind {
|
||||
Kind::Normal(s) => {
|
||||
if human {
|
||||
print!("{} ", HumanSize::from(s));
|
||||
} else {
|
||||
print!("{s:>10} ");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if human {
|
||||
print!("{:>7} ", "-");
|
||||
} else {
|
||||
print!("{:>10} ", "-");
|
||||
}
|
||||
}
|
||||
};
|
||||
match DateTime::from_timestamp(self.mtime as i64, 0) {
|
||||
Some(dt) => print!("{dt} "),
|
||||
_ => print!("{:>19} ", self.mtime),
|
||||
}
|
||||
match self.kind {
|
||||
Kind::Directory | Kind::Fifo | Kind::Normal(_) => println!("{}", self.name),
|
||||
Kind::HardLink(ref tgt) => println!("{}=>{}", self.name, tgt),
|
||||
Kind::SoftLink(ref tgt) => println!("{}->{}", self.name, tgt),
|
||||
Kind::Character(ref sp) | Kind::Block(ref sp) => {
|
||||
println!("{} {},{}", self.name, sp.major, sp.minor);
|
||||
}
|
||||
Kind::Eof => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Prints a line with relavent metadata included,
|
||||
/// colorizing the filename based on the filetype.
|
||||
/// Similar to GNU `ls --long --color auto`
|
||||
|
@ -299,7 +306,7 @@ impl Listing {
|
|||
/// Can return an `Error` if stdout is unavailable
|
||||
#[allow(clippy::cast_possible_wrap)]
|
||||
#[cfg(feature = "color")]
|
||||
pub fn print_color(&self) -> Result<(), Error> {
|
||||
pub fn print_color(&self, human: bool) -> Result<(), Error> {
|
||||
print!(
|
||||
"{}{}{}{}{}{}{}{}{}{} {:>4}:{:<4} ",
|
||||
match self.kind {
|
||||
|
@ -355,10 +362,22 @@ impl Listing {
|
|||
self.gid,
|
||||
);
|
||||
match self.kind {
|
||||
Kind::Normal(s) => print!("{s:>10} "),
|
||||
_ => print!("{:>10} ", "-"),
|
||||
Kind::Normal(s) => {
|
||||
if human {
|
||||
print!("{:>10} ", HumanSize::from(s));
|
||||
} else {
|
||||
print!("{s:>10} ");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if human {
|
||||
print!("{:>7} ", "-");
|
||||
} else {
|
||||
print!("{:>10} ", "-");
|
||||
}
|
||||
}
|
||||
}
|
||||
match NaiveDateTime::from_timestamp_opt(self.mtime as i64, 0) {
|
||||
match DateTime::from_timestamp(self.mtime as i64, 0) {
|
||||
Some(dt) => print!("{dt} "),
|
||||
_ => print!("{:>19} ", self.mtime),
|
||||
}
|
||||
|
|
124
src/stream.rs
124
src/stream.rs
|
@ -1,11 +1,5 @@
|
|||
#![allow(clippy::similar_names)]
|
||||
|
||||
#[cfg(feature = "parallel")]
|
||||
use {
|
||||
crate::FileType,
|
||||
rayon::{iter::ParallelBridge, prelude::ParallelIterator},
|
||||
std::sync::mpsc::Sender,
|
||||
};
|
||||
use {
|
||||
crate::{Error, Node, MAGIC},
|
||||
std::{
|
||||
|
@ -15,6 +9,12 @@ use {
|
|||
os::fd::{AsRawFd, FromRawFd},
|
||||
},
|
||||
};
|
||||
#[cfg(feature = "parallel")]
|
||||
use {
|
||||
crate::{FileType, Listing},
|
||||
rayon::{iter::ParallelBridge, prelude::ParallelIterator},
|
||||
std::sync::mpsc::Sender,
|
||||
};
|
||||
|
||||
/// An iterator over a series of archive `Node`'s. This struct is generic over any
|
||||
/// type which implements `Read`, such as a file or a network stream.
|
||||
|
@ -44,30 +44,8 @@ impl<R: Read> Iterator for Stream<R> {
|
|||
#[cfg(feature = "parallel")]
|
||||
#[derive(Debug)]
|
||||
pub enum Message {
|
||||
/// A File node has been successfully extracted
|
||||
FileExtracted {
|
||||
/// The path of the extracted `Node`
|
||||
name: String,
|
||||
/// The size in bytes of the created file
|
||||
size: u64,
|
||||
},
|
||||
/// A link has been created from a `Node`
|
||||
LinkCreated {
|
||||
/// The path of the link
|
||||
name: String,
|
||||
/// The path to this link's target
|
||||
target: String,
|
||||
},
|
||||
/// A directory has been created from this `Node`
|
||||
DirectoryCreated {
|
||||
/// The directory's path
|
||||
name: String,
|
||||
},
|
||||
/// A device file has been created from this `Node`
|
||||
DeviceCreated {
|
||||
/// The path of the device file
|
||||
name: String,
|
||||
},
|
||||
/// A Node has been successfully extracted
|
||||
NodeExtracted(Listing),
|
||||
/// An error occurred while extracting this `Node`
|
||||
Err {
|
||||
/// The pathame of the `Node` being extracted
|
||||
|
@ -135,12 +113,12 @@ impl<R: Read> Stream<R> {
|
|||
f: F,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
F: FnOnce(Node, Option<u32>, Option<u32>) + Copy,
|
||||
F: FnOnce(&Node, Option<u32>, Option<u32>) + Copy,
|
||||
{
|
||||
for node in self {
|
||||
let node = node?;
|
||||
node.extract(prefix, uid, gid)?;
|
||||
f(node, uid, gid);
|
||||
f(&node, uid, gid);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -148,7 +126,7 @@ impl<R: Read> Stream<R> {
|
|||
|
||||
#[cfg(feature = "parallel")]
|
||||
impl<R: Read + Send> Stream<R> {
|
||||
/// Extracts and archive in parallel
|
||||
/// Extracts an archive in parallel
|
||||
/// # Errors
|
||||
/// Returns `crate::Error` if io fails or several other error conditions
|
||||
pub fn par_extract(
|
||||
|
@ -170,35 +148,13 @@ impl<R: Read + Send> Stream<R> {
|
|||
return Err(error);
|
||||
}
|
||||
match n.filetype {
|
||||
FileType::Normal(f) => {
|
||||
s.send(Message::FileExtracted {
|
||||
name: n.name.clone(),
|
||||
size: f.len,
|
||||
})
|
||||
.map_err(|_| Error::SenderError)?;
|
||||
}
|
||||
FileType::SoftLink(t) | FileType::HardLink(t) => {
|
||||
s.send(Message::LinkCreated {
|
||||
name: n.name.clone(),
|
||||
target: t.clone(),
|
||||
})
|
||||
.map_err(|_| Error::SenderError)?;
|
||||
}
|
||||
FileType::Directory => {
|
||||
s.send(Message::DirectoryCreated {
|
||||
name: n.name.clone(),
|
||||
})
|
||||
.map_err(|_| Error::SenderError)?;
|
||||
}
|
||||
FileType::Block(_) | FileType::Character(_) | FileType::Fifo => {
|
||||
s.send(Message::DeviceCreated {
|
||||
name: n.name.clone(),
|
||||
})
|
||||
.map_err(|_| Error::SenderError)?;
|
||||
}
|
||||
FileType::Eof => {
|
||||
s.send(Message::Eof).map_err(|_| Error::SenderError)?;
|
||||
}
|
||||
_ => {
|
||||
s.send(Message::NodeExtracted(n.into()))
|
||||
.map_err(|_| Error::SenderError)?;
|
||||
}
|
||||
}
|
||||
Ok::<(), Error>(())
|
||||
})?;
|
||||
|
@ -206,7 +162,7 @@ impl<R: Read + Send> Stream<R> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Extracts and archive in parallel and runs the passed in function for
|
||||
/// Extracts an archive in parallel and runs the passed in function for
|
||||
/// each `Node`
|
||||
/// # Errors
|
||||
/// Returns `crate::Error` if io fails or several other error conditions
|
||||
|
@ -219,44 +175,26 @@ impl<R: Read + Send> Stream<R> {
|
|||
f: F,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
F: FnOnce(Node, Option<u32>, Option<u32>) + Copy + Send + Sync,
|
||||
F: FnOnce(&Node, Option<u32>, Option<u32>) + Copy + Send + Sync,
|
||||
{
|
||||
let s = sender.clone();
|
||||
self.into_iter().par_bridge().try_for_each_with(s, |s, n| {
|
||||
let n = n?;
|
||||
n.extract(prefix, uid, gid)?;
|
||||
match n.filetype {
|
||||
FileType::Normal(ref f) => {
|
||||
s.send(Message::FileExtracted {
|
||||
name: n.name.clone(),
|
||||
size: f.len,
|
||||
})
|
||||
.map_err(|_| Error::SenderError)?;
|
||||
}
|
||||
FileType::SoftLink(ref t) | FileType::HardLink(ref t) => {
|
||||
s.send(Message::LinkCreated {
|
||||
name: n.name.clone(),
|
||||
target: t.clone(),
|
||||
})
|
||||
.map_err(|_| Error::SenderError)?;
|
||||
}
|
||||
FileType::Directory => {
|
||||
s.send(Message::DirectoryCreated {
|
||||
name: n.name.clone(),
|
||||
})
|
||||
.map_err(|_| Error::SenderError)?;
|
||||
}
|
||||
FileType::Block(_) | FileType::Character(_) | FileType::Fifo => {
|
||||
s.send(Message::DeviceCreated {
|
||||
name: n.name.clone(),
|
||||
})
|
||||
.map_err(|_| Error::SenderError)?;
|
||||
}
|
||||
FileType::Eof => {
|
||||
s.send(Message::Eof).map_err(|_| Error::SenderError)?;
|
||||
}
|
||||
if let Err(error) = n.extract(prefix, uid, gid) {
|
||||
s.send(Message::Err {
|
||||
name: n.name.clone(),
|
||||
error: error.clone(),
|
||||
})
|
||||
.map_err(|_| Error::SenderError)?;
|
||||
return Err(error);
|
||||
}
|
||||
if let FileType::Eof = n.filetype {
|
||||
s.send(Message::Eof).map_err(|_| Error::SenderError)?;
|
||||
} else {
|
||||
f(&n, uid, gid);
|
||||
s.send(Message::NodeExtracted(n.into()))
|
||||
.map_err(|_| Error::SenderError)?;
|
||||
}
|
||||
f(n, uid, gid);
|
||||
Ok::<(), Error>(())
|
||||
})?;
|
||||
sender.send(Message::Eof).map_err(|_| Error::SenderError)?;
|
||||
|
|
Loading…
Add table
Reference in a new issue