use crate::prelude::*;
use lava_torrent::LavaTorrentError;
use lava_torrent::torrent::v1::Torrent as LavaTorrent;
pub struct TorrentVerifier;
impl TorrentVerifier {
pub async fn execute(
torrent_file: &Path,
directory: &Path,
) -> Result<Option<SourceIssue>, Failure<TorrentVerifyAction>> {
let start = Instant::now();
let torrent_file = torrent_file.to_path_buf();
let directory = directory.to_path_buf();
let result = spawn_blocking(move || verify(&torrent_file, &directory))
.await
.expect("torrent verify task should not panic");
trace!(
"{} torrent hash in {:.3}s",
"Checked".bold(),
start.elapsed().as_secs_f64()
);
result
}
}
fn verify(
torrent_file: &Path,
directory: &Path,
) -> Result<Option<SourceIssue>, Failure<TorrentVerifyAction>> {
let torrent = match LavaTorrent::read_from_file(torrent_file) {
Ok(torrent) => torrent,
Err(LavaTorrentError::MalformedTorrent(details)) => {
return Ok(Some(SourceIssue::InvalidTorrent {
details: details.into_owned(),
}));
}
Err(error) => {
return Err(Failure::wrap_with_path(
TorrentVerifyAction::ReadTorrent,
torrent_file,
)(error));
}
};
let mut hasher = match TorrentPieceHasher::open(&torrent, directory) {
Ok(hasher) => hasher,
Err(issue) => return Ok(Some(issue)),
};
trace!(
"Hashing {} pieces from {}",
torrent.pieces.len(),
directory.display()
);
for (index, expected) in torrent.pieces.iter().enumerate() {
match hasher.next() {
None => return Ok(Some(SourceIssue::HashCheck { piece_index: index })),
Some(Err(failure)) => return Err(failure),
Some(Ok(digest)) => {
if digest.as_slice() != expected.as_slice() {
return Ok(Some(SourceIssue::HashCheck { piece_index: index }));
}
}
}
}
match hasher.next() {
None => Ok(None),
Some(Ok(_)) => Ok(Some(SourceIssue::ExcessContent)),
Some(Err(failure)) => Err(failure),
}
}