Add progress messages and bar to imdl torrent verify
type: added
This commit is contained in:
parent
5a0bd2dda7
commit
1daa18ef9a
|
@ -48,7 +48,7 @@ pub(crate) use crate::{consts, error};
|
||||||
// traits
|
// traits
|
||||||
pub(crate) use crate::{
|
pub(crate) use crate::{
|
||||||
into_u64::IntoU64, into_usize::IntoUsize, path_ext::PathExt,
|
into_u64::IntoU64, into_usize::IntoUsize, path_ext::PathExt,
|
||||||
platform_interface::PlatformInterface, reckoner::Reckoner,
|
platform_interface::PlatformInterface, reckoner::Reckoner, step::Step,
|
||||||
};
|
};
|
||||||
|
|
||||||
// structs and enums
|
// structs and enums
|
||||||
|
|
|
@ -25,3 +25,44 @@ pub(crate) const AUTHOR: &str = env!("CARGO_PKG_AUTHORS");
|
||||||
pub(crate) const HELP_MESSAGE: &str = "Print help message.";
|
pub(crate) const HELP_MESSAGE: &str = "Print help message.";
|
||||||
|
|
||||||
pub(crate) const VERSION_MESSAGE: &str = "Print version number.";
|
pub(crate) const VERSION_MESSAGE: &str = "Print version number.";
|
||||||
|
|
||||||
|
/// The pogress chars are from the
|
||||||
|
/// [Block Elements unicode block](https://en.wikipedia.org/wiki/Block_Elements).
|
||||||
|
pub(crate) const PROGRESS_CHARS: &str = "█▉▊▋▌▍▎▏ ";
|
||||||
|
|
||||||
|
/// The tick chars are from the
|
||||||
|
/// [Braille Patterns unicode block](https://en.wikipedia.org/wiki/Braille_Patterns).
|
||||||
|
///
|
||||||
|
/// The chars are ordered to represent the 8 bit numbers in increasing
|
||||||
|
/// order. The individual braille cells represent bits, with empty cells
|
||||||
|
/// representing `0` and full cells representing `1`.
|
||||||
|
///
|
||||||
|
/// Digits are ordered from least significant to most significant from
|
||||||
|
/// top to bottom, and then left to right, like so:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// ╔═════╗
|
||||||
|
/// ║ 0 4 ║
|
||||||
|
/// ║ 1 5 ║
|
||||||
|
/// ║ 2 6 ║
|
||||||
|
/// ║ 3 7 ║
|
||||||
|
/// ╚═════╝
|
||||||
|
/// ```
|
||||||
|
pub(crate) const TICK_CHARS: &str = concat!(
|
||||||
|
"⠀⠁⠂⠃⠄⠅⠆⠇⡀⡁⡂⡃⡄⡅⡆⡇", // 0b0000----
|
||||||
|
"⠈⠉⠊⠋⠌⠍⠎⠏⡈⡉⡊⡋⡌⡍⡎⡏", // 0b0001----
|
||||||
|
"⠐⠑⠒⠓⠔⠕⠖⠗⡐⡑⡒⡓⡔⡕⡖⡗", // 0b0010----
|
||||||
|
"⠘⠙⠚⠛⠜⠝⠞⠟⡘⡙⡚⡛⡜⡝⡞⡟", // 0b0011----
|
||||||
|
"⠠⠡⠢⠣⠤⠥⠦⠧⡠⡡⡢⡣⡤⡥⡦⡧", // 0b0100----
|
||||||
|
"⠨⠩⠪⠫⠬⠭⠮⠯⡨⡩⡪⡫⡬⡭⡮⡯", // 0b0101----
|
||||||
|
"⠰⠱⠲⠳⠴⠵⠶⠷⡰⡱⡲⡳⡴⡵⡶⡷", // 0b0110----
|
||||||
|
"⠸⠹⠺⠻⠼⠽⠾⠿⡸⡹⡺⡻⡼⡽⡾⡿", // 0b0111----
|
||||||
|
"⢀⢁⢂⢃⢄⢅⢆⢇⣀⣁⣂⣃⣄⣅⣆⣇", // 0b1000----
|
||||||
|
"⢈⢉⢊⢋⢌⢍⢎⢏⣈⣉⣊⣋⣌⣍⣎⣏", // 0b1001----
|
||||||
|
"⢐⢑⢒⢓⢔⢕⢖⢗⣐⣑⣒⣓⣔⣕⣖⣗", // 0b1010----
|
||||||
|
"⢘⢙⢚⢛⢜⢝⢞⢟⣘⣙⣚⣛⣜⣝⣞⣟", // 0b1011----
|
||||||
|
"⢠⢡⢢⢣⢤⢥⢦⢧⣠⣡⣢⣣⣤⣥⣦⣧", // 0b1100----
|
||||||
|
"⢨⢩⢪⢫⢬⢭⢮⢯⣨⣩⣪⣫⣬⣭⣮⣯", // 0b1101----
|
||||||
|
"⢰⢱⢲⢳⢴⢵⢶⢷⣰⣱⣲⣳⣴⣵⣶⣷", // 0b1110----
|
||||||
|
"⢸⢹⢺⢻⢼⢽⢾⢿⣸⣹⣺⣻⣼⣽⣾⣿", // 0b1111----
|
||||||
|
);
|
||||||
|
|
|
@ -21,3 +21,9 @@ pub(crate) struct Info {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub(crate) mode: Mode,
|
pub(crate) mode: Mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Info {
|
||||||
|
pub(crate) fn content_size(&self) -> Bytes {
|
||||||
|
self.mode.content_size()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -79,6 +79,7 @@ mod platform_interface;
|
||||||
mod reckoner;
|
mod reckoner;
|
||||||
mod sha1_digest;
|
mod sha1_digest;
|
||||||
mod status;
|
mod status;
|
||||||
|
mod step;
|
||||||
mod style;
|
mod style;
|
||||||
mod subcommand;
|
mod subcommand;
|
||||||
mod table;
|
mod table;
|
||||||
|
|
|
@ -92,8 +92,12 @@ impl Metainfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn verify(&self, base: &Path) -> Result<Status> {
|
pub(crate) fn verify(&self, base: &Path, progress_bar: Option<ProgressBar>) -> Result<Status> {
|
||||||
Verifier::verify(self, base)
|
Verifier::verify(self, base, progress_bar)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn content_size(&self) -> Bytes {
|
||||||
|
self.info.content_size()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ pub(crate) enum Mode {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mode {
|
impl Mode {
|
||||||
pub(crate) fn total_size(&self) -> Bytes {
|
pub(crate) fn content_size(&self) -> Bytes {
|
||||||
match self {
|
match self {
|
||||||
Self::Single { length, .. } => *length,
|
Self::Single { length, .. } => *length,
|
||||||
Self::Multiple { files } => files.iter().map(|file| file.length).sum(),
|
Self::Multiple { files } => files.iter().map(|file| file.length).sum(),
|
||||||
|
|
34
src/step.rs
Normal file
34
src/step.rs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
use crate::common::*;
|
||||||
|
|
||||||
|
pub(crate) trait Step {
|
||||||
|
fn print(&self, env: &mut Env) -> Result<(), Error> {
|
||||||
|
let style = env.err_style();
|
||||||
|
let dim = style.dim();
|
||||||
|
let message = style.message();
|
||||||
|
|
||||||
|
err!(
|
||||||
|
env,
|
||||||
|
"{}[{}/{}]{} ",
|
||||||
|
dim.prefix(),
|
||||||
|
self.n(),
|
||||||
|
Self::total(),
|
||||||
|
dim.suffix()
|
||||||
|
)?;
|
||||||
|
|
||||||
|
err!(env, "{}{} ", message.prefix(), self.symbol())?;
|
||||||
|
|
||||||
|
self.write_message(&mut env.err).context(error::Stderr)?;
|
||||||
|
|
||||||
|
errln!(env, "{}", message.suffix())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn n(&self) -> usize;
|
||||||
|
|
||||||
|
fn total() -> usize;
|
||||||
|
|
||||||
|
fn write_message(&self, write: &mut dyn Write) -> io::Result<()>;
|
||||||
|
|
||||||
|
fn symbol(&self) -> &str;
|
||||||
|
}
|
|
@ -1,4 +1,7 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
use create_step::CreateStep;
|
||||||
|
|
||||||
|
mod create_step;
|
||||||
|
|
||||||
#[derive(StructOpt)]
|
#[derive(StructOpt)]
|
||||||
#[structopt(
|
#[structopt(
|
||||||
|
@ -203,12 +206,12 @@ impl Create {
|
||||||
announce_list.push(tier);
|
announce_list.push(tier);
|
||||||
}
|
}
|
||||||
|
|
||||||
Step::Searching.print(env)?;
|
CreateStep::Searching.print(env)?;
|
||||||
|
|
||||||
let spinner = if env.err_is_term() {
|
let spinner = if env.err_is_term() {
|
||||||
let style = ProgressStyle::default_spinner()
|
let style = ProgressStyle::default_spinner()
|
||||||
.template("{spinner:.green} {msg:.bold}…")
|
.template("{spinner:.green} {msg:.bold}…")
|
||||||
.tick_chars(&Self::tick_chars());
|
.tick_chars(consts::TICK_CHARS);
|
||||||
|
|
||||||
Some(ProgressBar::new_spinner().with_style(style))
|
Some(ProgressBar::new_spinner().with_style(style))
|
||||||
} else {
|
} else {
|
||||||
|
@ -295,7 +298,7 @@ impl Create {
|
||||||
Some(String::from(consts::CREATED_BY_DEFAULT))
|
Some(String::from(consts::CREATED_BY_DEFAULT))
|
||||||
};
|
};
|
||||||
|
|
||||||
Step::Hashing.print(env)?;
|
CreateStep::Hashing.print(env)?;
|
||||||
|
|
||||||
let progress_bar = if env.err_is_term() {
|
let progress_bar = if env.err_is_term() {
|
||||||
let style = ProgressStyle::default_bar()
|
let style = ProgressStyle::default_bar()
|
||||||
|
@ -303,8 +306,8 @@ impl Create {
|
||||||
"{spinner:.green} ⟪{elapsed_precise}⟫ ⟦{bar:40.cyan}⟧ \
|
"{spinner:.green} ⟪{elapsed_precise}⟫ ⟦{bar:40.cyan}⟧ \
|
||||||
{binary_bytes}/{binary_total_bytes} ⟨{binary_bytes_per_sec}, {eta}⟩",
|
{binary_bytes}/{binary_total_bytes} ⟨{binary_bytes_per_sec}, {eta}⟩",
|
||||||
)
|
)
|
||||||
.tick_chars(&Self::tick_chars())
|
.tick_chars(consts::TICK_CHARS)
|
||||||
.progress_chars("█▉▊▋▌▍▎▏ ");
|
.progress_chars(consts::PROGRESS_CHARS);
|
||||||
|
|
||||||
Some(ProgressBar::new(files.total_size().count()).with_style(style))
|
Some(ProgressBar::new(files.total_size().count()).with_style(style))
|
||||||
} else {
|
} else {
|
||||||
|
@ -318,7 +321,7 @@ impl Create {
|
||||||
progress_bar,
|
progress_bar,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Step::Writing { output: &output }.print(env)?;
|
CreateStep::Writing { output: &output }.print(env)?;
|
||||||
|
|
||||||
let info = Info {
|
let info = Info {
|
||||||
source: self.source,
|
source: self.source,
|
||||||
|
@ -374,7 +377,7 @@ impl Create {
|
||||||
|
|
||||||
assert_eq!(deserialized, metainfo);
|
assert_eq!(deserialized, metainfo);
|
||||||
|
|
||||||
let status = metainfo.verify(&input)?;
|
let status = metainfo.verify(&input, None)?;
|
||||||
|
|
||||||
if !status.good() {
|
if !status.good() {
|
||||||
return Err(Error::Verify { status });
|
return Err(Error::Verify { status });
|
||||||
|
@ -395,82 +398,6 @@ impl Create {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tick_chars() -> &'static str {
|
|
||||||
// The tick chars are from the Braille Patterns unicode block:
|
|
||||||
// https://en.wikipedia.org/wiki/Braille_Patterns
|
|
||||||
//
|
|
||||||
// The chars are ordered to represent the 8 bit numbers in increasing
|
|
||||||
// order. The individual braille cells represent bits, with empty cells
|
|
||||||
// representing `0` and full cells representing `1`.
|
|
||||||
//
|
|
||||||
// Digits are ordered from least significant to most significant from
|
|
||||||
// top to bottom, and then left to right, like so:
|
|
||||||
//
|
|
||||||
// ```
|
|
||||||
// ╔═════╗
|
|
||||||
// ║ 0 4 ║
|
|
||||||
// ║ 1 5 ║
|
|
||||||
// ║ 2 6 ║
|
|
||||||
// ║ 3 7 ║
|
|
||||||
// ╚═════╝
|
|
||||||
// ```
|
|
||||||
concat!(
|
|
||||||
"⠀⠁⠂⠃⠄⠅⠆⠇⡀⡁⡂⡃⡄⡅⡆⡇", // 0b0000----
|
|
||||||
"⠈⠉⠊⠋⠌⠍⠎⠏⡈⡉⡊⡋⡌⡍⡎⡏", // 0b0001----
|
|
||||||
"⠐⠑⠒⠓⠔⠕⠖⠗⡐⡑⡒⡓⡔⡕⡖⡗", // 0b0010----
|
|
||||||
"⠘⠙⠚⠛⠜⠝⠞⠟⡘⡙⡚⡛⡜⡝⡞⡟", // 0b0011----
|
|
||||||
"⠠⠡⠢⠣⠤⠥⠦⠧⡠⡡⡢⡣⡤⡥⡦⡧", // 0b0100----
|
|
||||||
"⠨⠩⠪⠫⠬⠭⠮⠯⡨⡩⡪⡫⡬⡭⡮⡯", // 0b0101----
|
|
||||||
"⠰⠱⠲⠳⠴⠵⠶⠷⡰⡱⡲⡳⡴⡵⡶⡷", // 0b0110----
|
|
||||||
"⠸⠹⠺⠻⠼⠽⠾⠿⡸⡹⡺⡻⡼⡽⡾⡿", // 0b0111----
|
|
||||||
"⢀⢁⢂⢃⢄⢅⢆⢇⣀⣁⣂⣃⣄⣅⣆⣇", // 0b1000----
|
|
||||||
"⢈⢉⢊⢋⢌⢍⢎⢏⣈⣉⣊⣋⣌⣍⣎⣏", // 0b1001----
|
|
||||||
"⢐⢑⢒⢓⢔⢕⢖⢗⣐⣑⣒⣓⣔⣕⣖⣗", // 0b1010----
|
|
||||||
"⢘⢙⢚⢛⢜⢝⢞⢟⣘⣙⣚⣛⣜⣝⣞⣟", // 0b1011----
|
|
||||||
"⢠⢡⢢⢣⢤⢥⢦⢧⣠⣡⣢⣣⣤⣥⣦⣧", // 0b1100----
|
|
||||||
"⢨⢩⢪⢫⢬⢭⢮⢯⣨⣩⣪⣫⣬⣭⣮⣯", // 0b1101----
|
|
||||||
"⢰⢱⢲⢳⢴⢵⢶⢷⣰⣱⣲⣳⣴⣵⣶⣷", // 0b1110----
|
|
||||||
"⢸⢹⢺⢻⢼⢽⢾⢿⣸⣹⣺⣻⣼⣽⣾⣿", // 0b1111----
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
enum Step<'output> {
|
|
||||||
Searching,
|
|
||||||
Hashing,
|
|
||||||
Writing { output: &'output OutputTarget },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'output> Step<'output> {
|
|
||||||
fn print(self, env: &mut Env) -> Result<(), Error> {
|
|
||||||
let style = env.err_style();
|
|
||||||
let dim = style.dim();
|
|
||||||
let message = style.message();
|
|
||||||
|
|
||||||
err!(env, "{}[{}/3]{} ", dim.prefix(), self.n(), dim.suffix())?;
|
|
||||||
|
|
||||||
err!(env, "{}", message.prefix())?;
|
|
||||||
|
|
||||||
match self {
|
|
||||||
Self::Searching => err!(env, "\u{1F9FF} Searching for files…")?,
|
|
||||||
Self::Hashing => err!(env, "\u{1F9EE} Hashing pieces…")?,
|
|
||||||
Self::Writing { output } => err!(env, "\u{1F4BE} Writing metainfo to {}…", output)?,
|
|
||||||
}
|
|
||||||
|
|
||||||
errln!(env, "{}", message.suffix())?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn n(self) -> usize {
|
|
||||||
match self {
|
|
||||||
Self::Searching => 1,
|
|
||||||
Self::Hashing => 2,
|
|
||||||
Self::Writing { .. } => 3,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
38
src/subcommand/torrent/create/create_step.rs
Normal file
38
src/subcommand/torrent/create/create_step.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
use crate::common::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub(crate) enum CreateStep<'output> {
|
||||||
|
Searching,
|
||||||
|
Hashing,
|
||||||
|
Writing { output: &'output OutputTarget },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'output> Step for CreateStep<'output> {
|
||||||
|
fn n(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Self::Searching => 1,
|
||||||
|
Self::Hashing => 2,
|
||||||
|
Self::Writing { .. } => 3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn symbol(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::Searching => "\u{1F9FF}",
|
||||||
|
Self::Hashing => "\u{1F9EE}",
|
||||||
|
Self::Writing { .. } => "\u{1F4BE}",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn total() -> usize {
|
||||||
|
3
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_message(&self, write: &mut dyn Write) -> io::Result<()> {
|
||||||
|
match self {
|
||||||
|
Self::Searching => write!(write, "Searching for files…"),
|
||||||
|
Self::Hashing => write!(write, "Hashing pieces…"),
|
||||||
|
Self::Writing { output } => write!(write, "Writing metainfo to {}…", output),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,7 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
use verify_step::VerifyStep;
|
||||||
|
|
||||||
|
mod verify_step;
|
||||||
|
|
||||||
#[derive(StructOpt)]
|
#[derive(StructOpt)]
|
||||||
#[structopt(
|
#[structopt(
|
||||||
|
@ -31,16 +34,40 @@ impl Verify {
|
||||||
let metainfo_path = env.resolve(&self.metainfo);
|
let metainfo_path = env.resolve(&self.metainfo);
|
||||||
let metainfo = Metainfo::load(&metainfo_path)?;
|
let metainfo = Metainfo::load(&metainfo_path)?;
|
||||||
|
|
||||||
|
VerifyStep::Loading {
|
||||||
|
metainfo: &metainfo_path,
|
||||||
|
}
|
||||||
|
.print(env)?;
|
||||||
|
|
||||||
let base = if let Some(content) = &self.content {
|
let base = if let Some(content) = &self.content {
|
||||||
env.resolve(content)
|
env.resolve(content)
|
||||||
} else {
|
} else {
|
||||||
metainfo_path.parent().unwrap().join(&metainfo.info.name)
|
metainfo_path.parent().unwrap().join(&metainfo.info.name)
|
||||||
};
|
};
|
||||||
|
|
||||||
let status = metainfo.verify(&base)?;
|
let progress_bar = if env.err_is_term() {
|
||||||
|
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)?;
|
||||||
|
|
||||||
if status.good() {
|
if status.good() {
|
||||||
errln!(env, "Verification succeeded.")?;
|
errln!(
|
||||||
|
env,
|
||||||
|
"\u{2728}\u{2728} Verification succeeded! \u{2728}\u{2728}"
|
||||||
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::Verify { status })
|
Err(Error::Verify { status })
|
||||||
|
@ -90,14 +117,22 @@ mod tests {
|
||||||
"torrent",
|
"torrent",
|
||||||
"verify",
|
"verify",
|
||||||
"--input",
|
"--input",
|
||||||
torrent,
|
&torrent,
|
||||||
],
|
],
|
||||||
tree: {},
|
tree: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_matches!(verify_env.run(), Ok(()));
|
assert_matches!(verify_env.run(), Ok(()));
|
||||||
|
|
||||||
assert_eq!(verify_env.err(), "Verification succeeded.\n");
|
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(), "");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -133,14 +168,21 @@ mod tests {
|
||||||
"torrent",
|
"torrent",
|
||||||
"verify",
|
"verify",
|
||||||
"--input",
|
"--input",
|
||||||
torrent,
|
&torrent,
|
||||||
],
|
],
|
||||||
tree: {},
|
tree: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_matches!(verify_env.run(), Err(Error::Verify { .. }));
|
assert_matches!(verify_env.run(), Err(Error::Verify { .. }));
|
||||||
|
|
||||||
assert_eq!(verify_env.err(), "");
|
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(), "");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -180,16 +222,24 @@ mod tests {
|
||||||
"torrent",
|
"torrent",
|
||||||
"verify",
|
"verify",
|
||||||
"--input",
|
"--input",
|
||||||
torrent,
|
&torrent,
|
||||||
"--content",
|
"--content",
|
||||||
bar,
|
&bar,
|
||||||
],
|
],
|
||||||
tree: {},
|
tree: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_matches!(verify_env.run(), Ok(()));
|
assert_matches!(verify_env.run(), Ok(()));
|
||||||
|
|
||||||
assert_eq!(verify_env.err(), "Verification succeeded.\n");
|
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(), "");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
38
src/subcommand/torrent/verify/verify_step.rs
Normal file
38
src/subcommand/torrent/verify/verify_step.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
use crate::common::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub(crate) enum VerifyStep<'a> {
|
||||||
|
Loading { metainfo: &'a Path },
|
||||||
|
Verifying { content: &'a Path },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Step for VerifyStep<'a> {
|
||||||
|
fn n(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Self::Loading { .. } => 1,
|
||||||
|
Self::Verifying { .. } => 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn symbol(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::Loading { .. } => "\u{1F4BE}",
|
||||||
|
Self::Verifying { .. } => "\u{1F9EE}",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn total() -> usize {
|
||||||
|
2
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_message(&self, write: &mut dyn Write) -> io::Result<()> {
|
||||||
|
match self {
|
||||||
|
Self::Loading { metainfo } => {
|
||||||
|
write!(write, "Loading metainfo from `{}`…", metainfo.display())
|
||||||
|
}
|
||||||
|
Self::Verifying { content } => {
|
||||||
|
write!(write, "Verifying pieces from `{}`…", content.display())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -95,7 +95,7 @@ impl TorrentSummary {
|
||||||
|
|
||||||
table.size("Torrent Size", self.size);
|
table.size("Torrent Size", self.size);
|
||||||
|
|
||||||
table.size("Content Size", self.metainfo.info.mode.total_size());
|
table.size("Content Size", self.metainfo.content_size());
|
||||||
|
|
||||||
table.row(
|
table.row(
|
||||||
"Private",
|
"Private",
|
||||||
|
|
|
@ -8,10 +8,15 @@ pub(crate) struct Verifier<'a> {
|
||||||
pieces: PieceList,
|
pieces: PieceList,
|
||||||
sha1: Sha1,
|
sha1: Sha1,
|
||||||
piece_bytes_hashed: usize,
|
piece_bytes_hashed: usize,
|
||||||
|
progress_bar: Option<ProgressBar>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Verifier<'a> {
|
impl<'a> Verifier<'a> {
|
||||||
fn new(metainfo: &'a Metainfo, base: &'a Path) -> Result<Verifier<'a>> {
|
fn new(
|
||||||
|
metainfo: &'a Metainfo,
|
||||||
|
base: &'a Path,
|
||||||
|
progress_bar: Option<ProgressBar>,
|
||||||
|
) -> Result<Verifier<'a>> {
|
||||||
let piece_length = metainfo.info.piece_length.as_piece_length()?.into_usize();
|
let piece_length = metainfo.info.piece_length.as_piece_length()?.into_usize();
|
||||||
|
|
||||||
Ok(Verifier {
|
Ok(Verifier {
|
||||||
|
@ -22,11 +27,16 @@ impl<'a> Verifier<'a> {
|
||||||
base,
|
base,
|
||||||
metainfo,
|
metainfo,
|
||||||
piece_length,
|
piece_length,
|
||||||
|
progress_bar,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn verify(metainfo: &'a Metainfo, base: &'a Path) -> Result<Status> {
|
pub(crate) fn verify(
|
||||||
Self::new(metainfo, base)?.verify_metainfo()
|
metainfo: &'a Metainfo,
|
||||||
|
base: &'a Path,
|
||||||
|
progress_bar: Option<ProgressBar>,
|
||||||
|
) -> Result<Status> {
|
||||||
|
Self::new(metainfo, base, progress_bar)?.verify_metainfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_metainfo(mut self) -> Result<Status> {
|
fn verify_metainfo(mut self) -> Result<Status> {
|
||||||
|
@ -76,6 +86,10 @@ impl<'a> Verifier<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
remaining -= buffer.len().into_u64();
|
remaining -= buffer.len().into_u64();
|
||||||
|
|
||||||
|
if let Some(progress_bar) = &self.progress_bar {
|
||||||
|
progress_bar.inc(to_buffer.into_u64());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -110,7 +124,7 @@ mod tests {
|
||||||
|
|
||||||
let metainfo = env.load_metainfo("foo.torrent");
|
let metainfo = env.load_metainfo("foo.torrent");
|
||||||
|
|
||||||
assert!(metainfo.verify(&env.resolve("foo"))?.good());
|
assert!(metainfo.verify(&env.resolve("foo"), None)?.good());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -141,7 +155,7 @@ mod tests {
|
||||||
|
|
||||||
let metainfo = env.load_metainfo("foo.torrent");
|
let metainfo = env.load_metainfo("foo.torrent");
|
||||||
|
|
||||||
let status = metainfo.verify(&env.resolve("foo"))?;
|
let status = metainfo.verify(&env.resolve("foo"), None)?;
|
||||||
|
|
||||||
assert!(status.files().iter().all(FileStatus::good));
|
assert!(status.files().iter().all(FileStatus::good));
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user