Just messing around with itertools Either
This commit is contained in:
parent
159e90b785
commit
be726e9aa3
3 changed files with 45 additions and 44 deletions
16
Cargo.lock
generated
16
Cargo.lock
generated
|
|
@ -438,6 +438,12 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.32"
|
version = "0.8.32"
|
||||||
|
|
@ -860,6 +866,15 @@ dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.8"
|
version = "1.0.8"
|
||||||
|
|
@ -935,6 +950,7 @@ dependencies = [
|
||||||
"buffers",
|
"buffers",
|
||||||
"clone_to_owned",
|
"clone_to_owned",
|
||||||
"hex 0.4.3",
|
"hex 0.4.3",
|
||||||
|
"itertools",
|
||||||
"log",
|
"log",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
|
||||||
|
|
@ -15,4 +15,5 @@ parking_lot = "0.12"
|
||||||
serde = {version = "1", features=["derive"]}
|
serde = {version = "1", features=["derive"]}
|
||||||
buffers = {path="../buffers"}
|
buffers = {path="../buffers"}
|
||||||
bencode = {path = "../bencode", default-features=false}
|
bencode = {path = "../bencode", default-features=false}
|
||||||
clone_to_owned = {path="../clone_to_owned"}
|
clone_to_owned = {path="../clone_to_owned"}
|
||||||
|
itertools = "0.11.0"
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
use std::path::PathBuf;
|
use std::{iter::once, path::PathBuf};
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use bencode::BencodeDeserializer;
|
use bencode::BencodeDeserializer;
|
||||||
use buffers::{ByteBuf, ByteString};
|
use buffers::{ByteBuf, ByteString};
|
||||||
use clone_to_owned::CloneToOwned;
|
use clone_to_owned::CloneToOwned;
|
||||||
|
use itertools::Either;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::id20::Id20;
|
use crate::id20::Id20;
|
||||||
|
|
@ -46,7 +47,7 @@ pub struct TorrentMetaV1<BufType> {
|
||||||
|
|
||||||
impl<BufType> TorrentMetaV1<BufType> {
|
impl<BufType> TorrentMetaV1<BufType> {
|
||||||
pub fn iter_announce(&self) -> impl Iterator<Item = &BufType> {
|
pub fn iter_announce(&self) -> impl Iterator<Item = &BufType> {
|
||||||
std::iter::once(&self.announce).chain(self.announce_list.iter().flatten())
|
once(&self.announce).chain(self.announce_list.iter().flatten())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,26 +113,13 @@ impl<'a, ByteBuf> FileIteratorName<'a, ByteBuf> {
|
||||||
where
|
where
|
||||||
ByteBuf: AsRef<[u8]>,
|
ByteBuf: AsRef<[u8]>,
|
||||||
{
|
{
|
||||||
let single_it = std::iter::once(match self {
|
let it = match self {
|
||||||
FileIteratorName::Single(n) => Some(*n),
|
FileIteratorName::Single(None) => return Either::Left(once(Ok("torrent-content"))),
|
||||||
FileIteratorName::Tree(_) => None,
|
FileIteratorName::Single(Some(name)) => Either::Left(once((*name).as_ref())),
|
||||||
});
|
FileIteratorName::Tree(t) => Either::Right(t.iter().map(|bb| bb.as_ref())),
|
||||||
let multi_it = match self {
|
};
|
||||||
FileIteratorName::Single(_) => &[],
|
Either::Right(it.map(|part: &'a [u8]| {
|
||||||
FileIteratorName::Tree(t) => *t,
|
let bit = std::str::from_utf8(part).context("cannot decode filename bit as UTF-8")?;
|
||||||
}
|
|
||||||
.iter()
|
|
||||||
.map(|p| Some(Some(p)));
|
|
||||||
|
|
||||||
let it = single_it.chain(multi_it).flatten();
|
|
||||||
|
|
||||||
it.map(|part| {
|
|
||||||
let part = match part {
|
|
||||||
Some(part) => part,
|
|
||||||
None => return Ok("torrent-content"),
|
|
||||||
};
|
|
||||||
let bit = std::str::from_utf8(part.as_ref())
|
|
||||||
.context("cannot decode filename bit as UTF-8")?;
|
|
||||||
if bit == ".." {
|
if bit == ".." {
|
||||||
anyhow::bail!("path traversal detected, \"..\" in filename bit {:?}", bit);
|
anyhow::bail!("path traversal detected, \"..\" in filename bit {:?}", bit);
|
||||||
}
|
}
|
||||||
|
|
@ -143,7 +131,7 @@ impl<'a, ByteBuf> FileIteratorName<'a, ByteBuf> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Ok(bit)
|
Ok(bit)
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -154,6 +142,7 @@ impl<BufType: AsRef<[u8]>> TorrentMetaV1Info<BufType> {
|
||||||
let expected_hash = self.pieces.as_ref().get(start..end)?;
|
let expected_hash = self.pieces.as_ref().get(start..end)?;
|
||||||
Some(expected_hash)
|
Some(expected_hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compare_hash(&self, piece: u32, hash: [u8; 20]) -> Option<bool> {
|
pub fn compare_hash(&self, piece: u32, hash: [u8; 20]) -> Option<bool> {
|
||||||
let start = piece as usize * 20;
|
let start = piece as usize * 20;
|
||||||
let end = start + 20;
|
let end = start + 20;
|
||||||
|
|
@ -161,36 +150,31 @@ impl<BufType: AsRef<[u8]>> TorrentMetaV1Info<BufType> {
|
||||||
Some(expected_hash == hash)
|
Some(expected_hash == hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_single_file(&self) -> anyhow::Result<bool> {
|
pub fn iter_filenames_and_lengths(
|
||||||
|
&self,
|
||||||
|
) -> anyhow::Result<impl Iterator<Item = (FileIteratorName<'_, BufType>, u64)>> {
|
||||||
match (self.length, self.files.as_ref()) {
|
match (self.length, self.files.as_ref()) {
|
||||||
|
// Single-file
|
||||||
|
(Some(length), None) => Ok(Either::Left(once((
|
||||||
|
FileIteratorName::Single(self.name.as_ref()),
|
||||||
|
length,
|
||||||
|
)))),
|
||||||
|
|
||||||
|
// Multi-file
|
||||||
(None, Some(files)) => {
|
(None, Some(files)) => {
|
||||||
if files.is_empty() {
|
if files.is_empty() {
|
||||||
anyhow::bail!("expected multi-file torrent to have at least one file")
|
anyhow::bail!("expected multi-file torrent to have at least one file")
|
||||||
}
|
}
|
||||||
Ok(false)
|
Ok(Either::Right(
|
||||||
|
files
|
||||||
|
.iter()
|
||||||
|
.map(|f| (FileIteratorName::Tree(&f.path), f.length)),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
(Some(_), None) => Ok(true),
|
|
||||||
_ => anyhow::bail!("torrent can't be both in single and multi-file mode"),
|
_ => anyhow::bail!("torrent can't be both in single and multi-file mode"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter_filenames_and_lengths(
|
|
||||||
&self,
|
|
||||||
) -> anyhow::Result<impl Iterator<Item = (FileIteratorName<'_, BufType>, u64)>> {
|
|
||||||
self.is_single_file()?;
|
|
||||||
|
|
||||||
let single_it = std::iter::once(match (self.name.as_ref(), self.length) {
|
|
||||||
(n, Some(l)) => Some((FileIteratorName::Single(n), l)),
|
|
||||||
_ => None,
|
|
||||||
});
|
|
||||||
let multi_it = self
|
|
||||||
.files
|
|
||||||
.as_deref()
|
|
||||||
.unwrap_or_default()
|
|
||||||
.iter()
|
|
||||||
.map(|f| Some((FileIteratorName::Tree(&f.path), f.length)));
|
|
||||||
Ok(single_it.chain(multi_it).flatten())
|
|
||||||
}
|
|
||||||
pub fn iter_file_lengths(&self) -> anyhow::Result<impl Iterator<Item = u64> + '_> {
|
pub fn iter_file_lengths(&self) -> anyhow::Result<impl Iterator<Item = u64> + '_> {
|
||||||
Ok(self.iter_filenames_and_lengths()?.map(|(_, l)| l))
|
Ok(self.iter_filenames_and_lengths()?.map(|(_, l)| l))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue