2020-02-14 00:12:49 -08:00
|
|
|
use crate::common::*;
|
2020-03-12 22:05:49 -07:00
|
|
|
use verify_step::VerifyStep;
|
|
|
|
|
|
|
|
mod verify_step;
|
2020-02-14 00:12:49 -08:00
|
|
|
|
|
|
|
#[derive(StructOpt)]
|
|
|
|
#[structopt(
|
|
|
|
help_message(consts::HELP_MESSAGE),
|
|
|
|
version_message(consts::VERSION_MESSAGE),
|
2020-02-14 04:04:17 -08:00
|
|
|
about("Verify files against a `.torrent` file.")
|
2020-02-14 00:12:49 -08:00
|
|
|
)]
|
|
|
|
pub(crate) struct Verify {
|
|
|
|
#[structopt(
|
2020-03-07 20:10:59 -08:00
|
|
|
long = "input",
|
|
|
|
short = "i",
|
|
|
|
value_name = "METAINFO",
|
2020-03-07 00:52:44 -08:00
|
|
|
help = "Verify torrent contents against torrent metainfo in `FILE`.",
|
2020-02-14 00:12:49 -08:00
|
|
|
parse(from_os_str)
|
|
|
|
)]
|
|
|
|
metainfo: PathBuf,
|
|
|
|
#[structopt(
|
2020-03-07 20:10:59 -08:00
|
|
|
long = "content",
|
|
|
|
short = "c",
|
2020-03-07 00:52:44 -08:00
|
|
|
value_name = "PATH",
|
2020-03-07 20:10:59 -08:00
|
|
|
help = "Verify torrent content at `PATH` against torrent metainfo. Defaults to `name` field \
|
2020-03-07 00:52:44 -08:00
|
|
|
of torrent info dictionary.",
|
2020-02-14 00:12:49 -08:00
|
|
|
parse(from_os_str)
|
|
|
|
)]
|
2020-03-07 20:10:59 -08:00
|
|
|
content: Option<PathBuf>,
|
2020-02-14 00:12:49 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Verify {
|
|
|
|
pub(crate) fn run(self, env: &mut Env) -> Result<(), Error> {
|
|
|
|
let metainfo_path = env.resolve(&self.metainfo);
|
|
|
|
let metainfo = Metainfo::load(&metainfo_path)?;
|
|
|
|
|
2020-03-12 22:05:49 -07:00
|
|
|
VerifyStep::Loading {
|
|
|
|
metainfo: &metainfo_path,
|
|
|
|
}
|
|
|
|
.print(env)?;
|
|
|
|
|
2020-03-07 20:10:59 -08:00
|
|
|
let base = if let Some(content) = &self.content {
|
|
|
|
env.resolve(content)
|
2020-02-14 00:12:49 -08:00
|
|
|
} else {
|
|
|
|
metainfo_path.parent().unwrap().join(&metainfo.info.name)
|
|
|
|
};
|
|
|
|
|
2020-03-15 03:22:33 -07:00
|
|
|
let progress_bar = if env.err().is_styled() {
|
2020-03-12 22:05:49 -07:00
|
|
|
let style = ProgressStyle::default_bar()
|
|
|
|
.template(
|
|
|
|
"{spinner:.green} ⟪{elapsed_precise}⟫ ⟦{bar:40.cyan}⟧ \
|
|
|
|
{binary_bytes}/{binary_total_bytes} ⟨{binary_bytes_per_sec}, {eta}⟩",
|
|
|
|
)
|
|
|
|
.tick_chars(consts::TICK_CHARS)
|
|
|
|
.progress_chars(consts::PROGRESS_CHARS);
|
|
|
|
|
|
|
|
Some(ProgressBar::new(metainfo.content_size().count()).with_style(style))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
|
|
|
VerifyStep::Verifying { content: &base }.print(env)?;
|
|
|
|
|
|
|
|
let status = metainfo.verify(&base, progress_bar)?;
|
2020-02-14 00:12:49 -08:00
|
|
|
|
|
|
|
if status.good() {
|
2020-03-12 22:05:49 -07:00
|
|
|
errln!(
|
|
|
|
env,
|
|
|
|
"\u{2728}\u{2728} Verification succeeded! \u{2728}\u{2728}"
|
|
|
|
)?;
|
2020-02-14 00:12:49 -08:00
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err(Error::Verify { status })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn require_metainfo_argument() {
|
2020-03-05 21:44:20 -08:00
|
|
|
let mut env = test_env! {
|
|
|
|
args: [],
|
|
|
|
tree: {},
|
|
|
|
};
|
2020-02-14 00:12:49 -08:00
|
|
|
assert!(matches!(env.run(), Err(Error::Clap { .. })));
|
|
|
|
}
|
2020-03-05 21:44:20 -08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn pass() -> Result<()> {
|
|
|
|
let mut create_env = test_env! {
|
|
|
|
args: [
|
|
|
|
"torrent",
|
|
|
|
"create",
|
|
|
|
"--input",
|
|
|
|
"foo",
|
|
|
|
"--announce",
|
|
|
|
"https://bar",
|
|
|
|
],
|
|
|
|
tree: {
|
|
|
|
foo: {
|
|
|
|
a: "abc",
|
|
|
|
d: "efg",
|
|
|
|
h: "ijk",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
create_env.run()?;
|
|
|
|
|
|
|
|
let torrent = create_env.resolve("foo.torrent");
|
|
|
|
|
|
|
|
let mut verify_env = test_env! {
|
|
|
|
args: [
|
|
|
|
"torrent",
|
|
|
|
"verify",
|
2020-03-07 20:10:59 -08:00
|
|
|
"--input",
|
2020-03-12 22:05:49 -07:00
|
|
|
&torrent,
|
2020-03-05 21:44:20 -08:00
|
|
|
],
|
|
|
|
tree: {},
|
|
|
|
};
|
|
|
|
|
|
|
|
assert_matches!(verify_env.run(), Ok(()));
|
|
|
|
|
2020-03-12 22:05:49 -07:00
|
|
|
let want = format!(
|
|
|
|
"[1/2] \u{1F4BE} Loading metainfo from `{}`…\n[2/2] \u{1F9EE} Verifying pieces from \
|
|
|
|
`{}`…\n\u{2728}\u{2728} Verification succeeded! \u{2728}\u{2728}\n",
|
|
|
|
torrent.display(),
|
|
|
|
create_env.resolve("foo").display()
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(verify_env.err(), want);
|
|
|
|
assert_eq!(verify_env.out(), "");
|
2020-03-05 21:44:20 -08:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn fail() -> Result<()> {
|
|
|
|
let mut create_env = test_env! {
|
|
|
|
args: [
|
|
|
|
"torrent",
|
|
|
|
"create",
|
|
|
|
"--input",
|
|
|
|
"foo",
|
|
|
|
"--announce",
|
|
|
|
"https://bar",
|
|
|
|
],
|
|
|
|
tree: {
|
|
|
|
foo: {
|
|
|
|
a: "abc",
|
|
|
|
d: "efg",
|
|
|
|
h: "ijk",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
create_env.run()?;
|
|
|
|
|
|
|
|
create_env.write("foo/a", "xyz");
|
|
|
|
|
|
|
|
let torrent = create_env.resolve("foo.torrent");
|
|
|
|
|
|
|
|
let mut verify_env = test_env! {
|
|
|
|
args: [
|
|
|
|
"torrent",
|
|
|
|
"verify",
|
2020-03-07 20:10:59 -08:00
|
|
|
"--input",
|
2020-03-12 22:05:49 -07:00
|
|
|
&torrent,
|
2020-03-05 21:44:20 -08:00
|
|
|
],
|
|
|
|
tree: {},
|
|
|
|
};
|
|
|
|
|
|
|
|
assert_matches!(verify_env.run(), Err(Error::Verify { .. }));
|
|
|
|
|
2020-03-12 22:05:49 -07:00
|
|
|
let want = format!(
|
|
|
|
"[1/2] \u{1F4BE} Loading metainfo from `{}`…\n[2/2] \u{1F9EE} Verifying pieces from `{}`…\n",
|
|
|
|
torrent.display(),
|
|
|
|
create_env.resolve("foo").display()
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(verify_env.err(), want);
|
|
|
|
assert_eq!(verify_env.out(), "");
|
2020-03-05 21:44:20 -08:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-03-07 20:10:59 -08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn alternate_path() -> Result<()> {
|
|
|
|
let mut create_env = test_env! {
|
|
|
|
args: [
|
|
|
|
"torrent",
|
|
|
|
"create",
|
|
|
|
"--input",
|
|
|
|
"foo",
|
|
|
|
"--announce",
|
|
|
|
"https://bar",
|
|
|
|
],
|
|
|
|
tree: {
|
|
|
|
foo: {
|
|
|
|
a: "abc",
|
|
|
|
d: "efg",
|
|
|
|
h: "ijk",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
create_env.run()?;
|
|
|
|
|
|
|
|
let torrent = create_env.resolve("foo.torrent");
|
|
|
|
|
|
|
|
let foo = create_env.resolve("foo");
|
|
|
|
|
|
|
|
let bar = create_env.resolve("bar");
|
|
|
|
|
|
|
|
fs::rename(&foo, &bar).unwrap();
|
|
|
|
|
|
|
|
let mut verify_env = test_env! {
|
|
|
|
args: [
|
|
|
|
"torrent",
|
|
|
|
"verify",
|
|
|
|
"--input",
|
2020-03-12 22:05:49 -07:00
|
|
|
&torrent,
|
2020-03-07 20:10:59 -08:00
|
|
|
"--content",
|
2020-03-12 22:05:49 -07:00
|
|
|
&bar,
|
2020-03-07 20:10:59 -08:00
|
|
|
],
|
|
|
|
tree: {},
|
|
|
|
};
|
|
|
|
|
|
|
|
assert_matches!(verify_env.run(), Ok(()));
|
|
|
|
|
2020-03-12 22:05:49 -07:00
|
|
|
let want = format!(
|
|
|
|
"[1/2] \u{1F4BE} Loading metainfo from `{}`…\n[2/2] \u{1F9EE} Verifying pieces from \
|
|
|
|
`{}`…\n\u{2728}\u{2728} Verification succeeded! \u{2728}\u{2728}\n",
|
|
|
|
torrent.display(),
|
|
|
|
bar.display(),
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(verify_env.err(), want);
|
|
|
|
assert_eq!(verify_env.out(), "");
|
2020-03-07 20:10:59 -08:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-02-14 00:12:49 -08:00
|
|
|
}
|