1
0
Fork 0
mirror of https://github.com/BLAKE3-team/BLAKE3 synced 2024-05-08 19:06:02 +02:00

Rewrite CLI using Derive API

This commit is contained in:
Shun Sakai 2022-09-14 19:53:05 +09:00
parent 7e15c5314e
commit df7136837a
4 changed files with 232 additions and 184 deletions

191
b3sum/Cargo.lock generated
View File

@ -4,9 +4,9 @@ version = 3
[[package]]
name = "anyhow"
version = "1.0.56"
version = "1.0.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27"
checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602"
[[package]]
name = "arrayref"
@ -73,9 +73,9 @@ dependencies = [
[[package]]
name = "block-buffer"
version = "0.10.2"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
dependencies = [
"generic-array",
]
@ -94,19 +94,43 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "3.1.6"
version = "3.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8c93436c21e4698bacadf42917db28b23017027a4deccb35dbe47a7e7840123"
checksum = "1ed5341b2301a26ab80be5cbdced622e80ed808483c52e45e3310a877d3b37d7"
dependencies = [
"atty",
"bitflags",
"clap_derive",
"clap_lex",
"indexmap",
"os_str_bytes",
"once_cell",
"strsim",
"termcolor",
"textwrap",
]
[[package]]
name = "clap_derive"
version = "3.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "constant_time_eq"
version = "0.1.5"
@ -115,9 +139,9 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "crossbeam-channel"
version = "0.5.4"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
dependencies = [
"cfg-if",
"crossbeam-utils",
@ -125,9 +149,9 @@ dependencies = [
[[package]]
name = "crossbeam-deque"
version = "0.8.1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
dependencies = [
"cfg-if",
"crossbeam-epoch",
@ -136,33 +160,33 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
version = "0.9.8"
version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c"
checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"lazy_static",
"memoffset",
"once_cell",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.8"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
dependencies = [
"cfg-if",
"lazy_static",
"once_cell",
]
[[package]]
name = "crypto-common"
version = "0.1.3"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
@ -193,24 +217,24 @@ dependencies = [
[[package]]
name = "either"
version = "1.6.1"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]]
name = "fastrand"
version = "1.7.0"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
dependencies = [
"instant",
]
[[package]]
name = "generic-array"
version = "0.14.5"
version = "0.14.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
dependencies = [
"typenum",
"version_check",
@ -224,9 +248,15 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "hashbrown"
version = "0.11.2"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "heck"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
name = "hermit-abi"
@ -245,9 +275,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "indexmap"
version = "1.8.0"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
dependencies = [
"autocfg",
"hashbrown",
@ -262,29 +292,17 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.121"
version = "0.2.132"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
[[package]]
name = "memmap2"
version = "0.5.3"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "057a3db23999c867821a7a59feb06a578fcb03685e983dff90daf9e7d24ac08f"
checksum = "95af15f345b17af2efc8ead6080fb8bc376f8cec1b35277b935637595fe77498"
dependencies = [
"libc",
]
@ -310,9 +328,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.10.0"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0"
[[package]]
name = "os_pipe"
@ -326,18 +344,57 @@ dependencies = [
[[package]]
name = "os_str_bytes"
version = "6.0.0"
version = "6.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"memchr",
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rayon"
version = "1.5.1"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90"
checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d"
dependencies = [
"autocfg",
"crossbeam-deque",
@ -347,22 +404,21 @@ dependencies = [
[[package]]
name = "rayon-core"
version = "1.9.1"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e"
checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
"lazy_static",
"num_cpus",
]
[[package]]
name = "redox_syscall"
version = "0.2.12"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ae183fc1b06c149f0c1793e1eb447c8b04bfe46d48e9e48bfb8d2d7ed64ecf0"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags",
]
@ -404,6 +460,17 @@ version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "syn"
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tempfile"
version = "3.3.0"
@ -439,6 +506,12 @@ version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "unicode-ident"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
[[package]]
name = "version_check"
version = "0.9.4"
@ -447,9 +520,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wild"
version = "2.0.4"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "035793abb854745033f01a07647a79831eba29ec0be377205f2a25b0aa830020"
checksum = "05b116685a6be0c52f5a103334cbff26db643826c7b3735fc0a3ba9871310a74"
dependencies = [
"glob",
]

View File

@ -16,7 +16,7 @@ pure = ["blake3/pure"]
[dependencies]
anyhow = "1.0.25"
blake3 = { version = "1", path = "..", features = ["rayon"] }
clap = "3.0.5"
clap = { version = "3.2.21", features = ["derive"] }
hex = "0.4.0"
memmap2 = "0.5.3"
rayon = "1.2.1"

View File

@ -22,7 +22,7 @@ OPTIONS:
--keyed Uses the keyed mode. The secret key is read from standard
input, and it must be exactly 32 raw bytes.
-l, --length <LEN> The number of output bytes, prior to hex
encoding (default 32)
encoding [default: 32]
--no-mmap Disables memory mapping. Currently this also disables
multithreading.
--no-names Omits filenames in the output

View File

@ -1,5 +1,5 @@
use anyhow::{bail, ensure, Context, Result};
use clap::{Arg, Command};
use anyhow::{bail, ensure, Result};
use clap::Parser;
use std::cmp;
use std::convert::TryInto;
use std::fs::File;
@ -12,124 +12,111 @@ mod unit_tests;
const NAME: &str = "b3sum";
const FILE_ARG: &str = "FILE";
const DERIVE_KEY_ARG: &str = "derive-key";
const KEYED_ARG: &str = "keyed";
const LENGTH_ARG: &str = "length";
const NO_MMAP_ARG: &str = "no-mmap";
const NO_NAMES_ARG: &str = "no-names";
const NUM_THREADS_ARG: &str = "num-threads";
const RAW_ARG: &str = "raw";
const CHECK_ARG: &str = "check";
const QUIET_ARG: &str = "quiet";
#[derive(Parser)]
#[clap(version)]
struct Inner {
/// Files to hash, or checkfiles to check. When no file is given,
/// or when - is given, read standard input.
#[clap(value_parser, verbatim_doc_comment)]
file: Vec<PathBuf>,
/// The number of output bytes, prior to hex
/// encoding
#[clap(
long,
short,
value_name("LEN"),
default_value_t = blake3::OUT_LEN as u64,
verbatim_doc_comment
)]
length: u64,
/// The maximum number of threads to use. By
/// default, this is the number of logical cores.
/// If this flag is omitted, or if its value is 0,
/// RAYON_NUM_THREADS is also respected.
#[clap(long, value_name("NUM"), verbatim_doc_comment)]
num_threads: Option<usize>,
/// Uses the keyed mode. The secret key is read from standard
/// input, and it must be exactly 32 raw bytes.
#[clap(long, requires("file"), verbatim_doc_comment)]
keyed: bool,
/// Uses the key derivation mode, with the given
/// context string. Cannot be used with --keyed.
#[clap(
long,
conflicts_with(KEYED_ARG),
value_name("CONTEXT"),
verbatim_doc_comment
)]
derive_key: Option<String>,
/// Disables memory mapping. Currently this also disables
/// multithreading.
#[clap(long, verbatim_doc_comment)]
no_mmap: bool,
/// Omits filenames in the output
#[clap(long)]
no_names: bool,
/// Writes raw output bytes to stdout, rather than hex.
/// --no-names is implied. In this case, only a single
/// input is allowed.
#[clap(long, verbatim_doc_comment)]
raw: bool,
/// Reads BLAKE3 sums from the [FILE]s and checks them
#[clap(
long,
short,
conflicts_with(DERIVE_KEY_ARG),
conflicts_with(KEYED_ARG),
conflicts_with(LENGTH_ARG),
conflicts_with(RAW_ARG),
conflicts_with(NO_NAMES_ARG)
)]
check: bool,
/// Skips printing OK for each successfully verified file.
/// Must be used with --check.
#[clap(long, requires(CHECK_ARG), verbatim_doc_comment)]
quiet: bool,
}
struct Args {
inner: clap::ArgMatches,
inner: Inner,
file_args: Vec<PathBuf>,
base_hasher: blake3::Hasher,
}
impl Args {
fn parse() -> Result<Self> {
let inner = Command::new(NAME)
.version(env!("CARGO_PKG_VERSION"))
.arg(
Arg::new(FILE_ARG)
.multiple_occurrences(true)
.allow_invalid_utf8(true)
.help(
"Files to hash, or checkfiles to check. When no file is given,\n\
or when - is given, read standard input.",
),
)
.arg(
Arg::new(LENGTH_ARG)
.long(LENGTH_ARG)
.short('l')
.takes_value(true)
.value_name("LEN")
.help(
"The number of output bytes, prior to hex\n\
encoding (default 32)",
),
)
.arg(
Arg::new(NUM_THREADS_ARG)
.long(NUM_THREADS_ARG)
.takes_value(true)
.value_name("NUM")
.help(
"The maximum number of threads to use. By\n\
default, this is the number of logical cores.\n\
If this flag is omitted, or if its value is 0,\n\
RAYON_NUM_THREADS is also respected.",
),
)
.arg(Arg::new(KEYED_ARG).long(KEYED_ARG).requires(FILE_ARG).help(
"Uses the keyed mode. The secret key is read from standard\n\
input, and it must be exactly 32 raw bytes.",
))
.arg(
Arg::new(DERIVE_KEY_ARG)
.long(DERIVE_KEY_ARG)
.conflicts_with(KEYED_ARG)
.takes_value(true)
.value_name("CONTEXT")
.help(
"Uses the key derivation mode, with the given\n\
context string. Cannot be used with --keyed.",
),
)
.arg(Arg::new(NO_MMAP_ARG).long(NO_MMAP_ARG).help(
"Disables memory mapping. Currently this also disables\n\
multithreading.",
))
.arg(
Arg::new(NO_NAMES_ARG)
.long(NO_NAMES_ARG)
.help("Omits filenames in the output"),
)
.arg(Arg::new(RAW_ARG).long(RAW_ARG).help(
"Writes raw output bytes to stdout, rather than hex.\n\
--no-names is implied. In this case, only a single\n\
input is allowed.",
))
.arg(
Arg::new(CHECK_ARG)
.long(CHECK_ARG)
.short('c')
.conflicts_with(DERIVE_KEY_ARG)
.conflicts_with(KEYED_ARG)
.conflicts_with(LENGTH_ARG)
.conflicts_with(RAW_ARG)
.conflicts_with(NO_NAMES_ARG)
.help("Reads BLAKE3 sums from the [FILE]s and checks them"),
)
.arg(
Arg::new(QUIET_ARG)
.long(QUIET_ARG)
.requires(CHECK_ARG)
.help(
"Skips printing OK for each successfully verified file.\n\
Must be used with --check.",
),
)
// wild::args_os() is equivalent to std::env::args_os() on Unix,
// but on Windows it adds support for globbing.
.get_matches_from(wild::args_os());
let file_args = if let Some(iter) = inner.values_of_os(FILE_ARG) {
iter.map(|s| s.into()).collect()
// wild::args_os() is equivalent to std::env::args_os() on Unix,
// but on Windows it adds support for globbing.
let inner = Inner::parse_from(wild::args_os());
let file_args = if !inner.file.is_empty() {
inner.file.clone()
} else {
vec!["-".into()]
};
if inner.is_present(RAW_ARG) && file_args.len() > 1 {
if inner.raw && file_args.len() > 1 {
bail!("Only one filename can be provided when using --raw");
}
let base_hasher = if inner.is_present(KEYED_ARG) {
let base_hasher = if inner.keyed {
// In keyed mode, since stdin is used for the key, we can't handle
// `-` arguments. Input::open handles that case below.
blake3::Hasher::new_keyed(&read_key_from_stdin()?)
} else if let Some(context) = inner.value_of(DERIVE_KEY_ARG) {
} else if let Some(ref context) = inner.derive_key {
blake3::Hasher::new_derive_key(context)
} else {
blake3::Hasher::new()
@ -141,48 +128,36 @@ impl Args {
})
}
fn num_threads(&self) -> Result<Option<usize>> {
if let Some(num_threads_str) = self.inner.value_of(NUM_THREADS_ARG) {
Ok(Some(
num_threads_str
.parse()
.context("Failed to parse num threads.")?,
))
} else {
Ok(None)
}
fn num_threads(&self) -> Option<usize> {
self.inner.num_threads
}
fn check(&self) -> bool {
self.inner.is_present(CHECK_ARG)
self.inner.check
}
fn raw(&self) -> bool {
self.inner.is_present(RAW_ARG)
self.inner.raw
}
fn no_mmap(&self) -> bool {
self.inner.is_present(NO_MMAP_ARG)
self.inner.no_mmap
}
fn no_names(&self) -> bool {
self.inner.is_present(NO_NAMES_ARG)
self.inner.no_names
}
fn len(&self) -> Result<u64> {
if let Some(length) = self.inner.value_of(LENGTH_ARG) {
length.parse::<u64>().context("Failed to parse length.")
} else {
Ok(blake3::OUT_LEN as u64)
}
fn len(&self) -> u64 {
self.inner.length
}
fn keyed(&self) -> bool {
self.inner.is_present(KEYED_ARG)
self.inner.keyed
}
fn quiet(&self) -> bool {
self.inner.is_present(QUIET_ARG)
self.inner.quiet
}
}
@ -307,7 +282,7 @@ fn maybe_memmap_file(file: &File) -> Result<Option<memmap2::Mmap>> {
fn write_hex_output(mut output: blake3::OutputReader, args: &Args) -> Result<()> {
// Encoding multiples of the block size is most efficient.
let mut len = args.len()?;
let mut len = args.len();
let mut block = [0; blake3::guts::BLOCK_LEN];
while len > 0 {
output.fill(&mut block);
@ -320,7 +295,7 @@ fn write_hex_output(mut output: blake3::OutputReader, args: &Args) -> Result<()>
}
fn write_raw_output(output: blake3::OutputReader, args: &Args) -> Result<()> {
let mut output = output.take(args.len()?);
let mut output = output.take(args.len());
let stdout = std::io::stdout();
let mut handler = stdout.lock();
std::io::copy(&mut output, &mut handler)?;
@ -589,7 +564,7 @@ fn check_one_checkfile(path: &Path, args: &Args, some_file_failed: &mut bool) ->
fn main() -> Result<()> {
let args = Args::parse()?;
let mut thread_pool_builder = rayon::ThreadPoolBuilder::new();
if let Some(num_threads) = args.num_threads()? {
if let Some(num_threads) = args.num_threads() {
thread_pool_builder = thread_pool_builder.num_threads(num_threads);
}
let thread_pool = thread_pool_builder.build()?;