Drop serde_bencode
in favor of bendy
For now depend on my branch on Github, until serde support and the value type land in the main repo. type: reform
This commit is contained in:
parent
9b696f78a7
commit
b0f449b6ae
106
Cargo.lock
generated
106
Cargo.lock
generated
|
@ -44,6 +44,38 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "backtrace"
|
||||||
|
version = "0.3.43"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f80256bc78f67e7df7e36d77366f636ed976895d91fe2ab9efa3973e8fe8c4f"
|
||||||
|
dependencies = [
|
||||||
|
"backtrace-sys",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"rustc-demangle",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "backtrace-sys"
|
||||||
|
version = "0.1.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bendy"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "git+https://github.com/casey/bendy.git?branch=value#8009217c7c753e0c3e1b6685bd9e9f980e5d38de"
|
||||||
|
dependencies = [
|
||||||
|
"failure",
|
||||||
|
"serde",
|
||||||
|
"serde_bytes 0.11.3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
|
@ -68,6 +100,12 @@ dependencies = [
|
||||||
"ppv-lite86",
|
"ppv-lite86",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.50"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "0.1.10"
|
version = "0.1.10"
|
||||||
|
@ -136,6 +174,28 @@ dependencies = [
|
||||||
"termcolor",
|
"termcolor",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "failure"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9"
|
||||||
|
dependencies = [
|
||||||
|
"backtrace",
|
||||||
|
"failure_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "failure_derive"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"synstructure",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fnv"
|
name = "fnv"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
|
@ -216,6 +276,7 @@ version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term 0.12.1",
|
"ansi_term 0.12.1",
|
||||||
"atty",
|
"atty",
|
||||||
|
"bendy",
|
||||||
"chrono",
|
"chrono",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"globset",
|
"globset",
|
||||||
|
@ -226,10 +287,12 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_bencode",
|
"serde_bencode",
|
||||||
"serde_bytes 0.11.3",
|
"serde_bytes 0.11.3",
|
||||||
|
"serde_with",
|
||||||
"sha1",
|
"sha1",
|
||||||
"snafu",
|
"snafu",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
"structopt",
|
"structopt",
|
||||||
|
"syn",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
"url",
|
"url",
|
||||||
|
@ -461,6 +524,12 @@ dependencies = [
|
||||||
"winapi 0.3.8",
|
"winapi 0.3.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-demangle"
|
||||||
|
version = "0.1.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
|
@ -492,9 +561,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_bencode"
|
name = "serde_bencode"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9b79ce11369638af2fea08e00390316304c5b5e1b7a53d58ace925152b657443"
|
checksum = "315c49c11b6b10acc209df75b757ee70957b911ecd0e29bcbf2b735ebd580d45"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_bytes 0.10.5",
|
"serde_bytes 0.10.5",
|
||||||
|
@ -529,6 +598,27 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_with"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "89d3d595d64120bbbc70b7f6d5ae63298b62a3d9f373ec2f56acf5365ca8a444"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_with_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_with_macros"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4070d2c9b9d258465ad1d82aabb985b84cd9a3afa94da25ece5a9938ba5f1606"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha1"
|
name = "sha1"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
|
@ -620,6 +710,18 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "synstructure"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.1.0"
|
version = "3.1.0"
|
||||||
|
|
|
@ -24,14 +24,22 @@ pretty_assertions = "0.6"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
serde_bencode = "0.2"
|
serde_bencode = "0.2"
|
||||||
serde_bytes = "0.11"
|
serde_bytes = "0.11"
|
||||||
|
serde_with = "1.4"
|
||||||
sha1 = "0.6"
|
sha1 = "0.6"
|
||||||
snafu = "0.6"
|
snafu = "0.6"
|
||||||
static_assertions = "1"
|
static_assertions = "1"
|
||||||
|
syn = "1.0.14"
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
unicode-width = "0.1"
|
unicode-width = "0.1"
|
||||||
url = "2"
|
url = "2"
|
||||||
walkdir = "2.1"
|
walkdir = "2.1"
|
||||||
|
|
||||||
|
[dependencies.bendy]
|
||||||
|
version = "0.2.2"
|
||||||
|
git = "https://github.com/casey/bendy.git"
|
||||||
|
branch = "value"
|
||||||
|
features = ["serde"]
|
||||||
|
|
||||||
[dependencies.serde]
|
[dependencies.serde]
|
||||||
version = "1.0.103"
|
version = "1.0.103"
|
||||||
features = ["derive"]
|
features = ["derive"]
|
||||||
|
|
378
src/bencode.rs
378
src/bencode.rs
|
@ -1,378 +0,0 @@
|
||||||
use crate::common::*;
|
|
||||||
|
|
||||||
use self::Error::*;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub(crate) enum Value<'buffer> {
|
|
||||||
Int(&'buffer str),
|
|
||||||
List(Vec<Value<'buffer>>),
|
|
||||||
Dict(Vec<(&'buffer [u8], Value<'buffer>)>),
|
|
||||||
Str(&'buffer [u8]),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'buffer> Value<'buffer> {
|
|
||||||
pub(crate) fn decode(buffer: &'buffer [u8]) -> Result<Value<'buffer>, Error> {
|
|
||||||
Parser::parse(buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn encode(&self) -> Vec<u8> {
|
|
||||||
let mut buffer = Vec::new();
|
|
||||||
self.encode_into(&mut buffer);
|
|
||||||
buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn encode_into(&self, buffer: &mut Vec<u8>) {
|
|
||||||
match self {
|
|
||||||
Self::Int(value) => {
|
|
||||||
buffer.push(b'i');
|
|
||||||
buffer.extend_from_slice(value.as_bytes());
|
|
||||||
buffer.push(b'e');
|
|
||||||
}
|
|
||||||
Self::List(values) => {
|
|
||||||
buffer.push(b'l');
|
|
||||||
for value in values {
|
|
||||||
value.encode_into(buffer);
|
|
||||||
}
|
|
||||||
buffer.push(b'e');
|
|
||||||
}
|
|
||||||
Self::Dict(items) => {
|
|
||||||
buffer.push(b'd');
|
|
||||||
for (key, value) in items {
|
|
||||||
Self::encode_str(buffer, key);
|
|
||||||
value.encode_into(buffer);
|
|
||||||
}
|
|
||||||
buffer.push(b'e');
|
|
||||||
}
|
|
||||||
Self::Str(contents) => Self::encode_str(buffer, contents),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encode_str(buffer: &mut Vec<u8>, contents: &[u8]) {
|
|
||||||
buffer.extend_from_slice(contents.len().to_string().as_bytes());
|
|
||||||
buffer.push(b':');
|
|
||||||
buffer.extend_from_slice(contents);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fmt_str(f: &mut Formatter, contents: &[u8]) -> fmt::Result {
|
|
||||||
if let Ok(text) = str::from_utf8(contents) {
|
|
||||||
write!(f, "\"{}\"", text)
|
|
||||||
} else {
|
|
||||||
write!(f, "<")?;
|
|
||||||
for byte in contents {
|
|
||||||
write!(f, "{:X}", byte)?;
|
|
||||||
}
|
|
||||||
write!(f, ">")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'buffer> Display for Value<'buffer> {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::Int(value) => write!(f, "{}", value),
|
|
||||||
Self::List(values) => {
|
|
||||||
write!(f, "[")?;
|
|
||||||
for (i, value) in values.iter().enumerate() {
|
|
||||||
if i > 0 {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
|
||||||
write!(f, "{}", value)?;
|
|
||||||
}
|
|
||||||
write!(f, "]")
|
|
||||||
}
|
|
||||||
Self::Dict(items) => {
|
|
||||||
write!(f, "{{")?;
|
|
||||||
for (i, (key, value)) in items.iter().enumerate() {
|
|
||||||
if i > 0 {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
|
||||||
Value::fmt_str(f, key)?;
|
|
||||||
write!(f, ": {}", value)?;
|
|
||||||
}
|
|
||||||
write!(f, "}}")
|
|
||||||
}
|
|
||||||
Self::Str(contents) => Value::fmt_str(f, contents),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub(crate) enum Error {
|
|
||||||
TrailingData { start: usize },
|
|
||||||
UnexpectedEndOfBuffer,
|
|
||||||
UnexpectedByte { found: u8 },
|
|
||||||
UnsortedKey,
|
|
||||||
DuplicateKey,
|
|
||||||
EmptyInteger,
|
|
||||||
NegativeZero,
|
|
||||||
LeadingZero,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct Parser<'buffer> {
|
|
||||||
index: usize,
|
|
||||||
buffer: &'buffer [u8],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'buffer> Parser<'buffer> {
|
|
||||||
pub(crate) fn parse(buffer: &'buffer [u8]) -> Result<Value<'buffer>, Error> {
|
|
||||||
let parser = Parser { index: 0, buffer };
|
|
||||||
|
|
||||||
Ok(parser.root()?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn root(mut self) -> Result<Value<'buffer>, Error> {
|
|
||||||
let root = self.value()?;
|
|
||||||
|
|
||||||
if self.index != self.buffer.len() {
|
|
||||||
return Err(TrailingData { start: self.index });
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(root)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn value(&mut self) -> Result<Value<'buffer>, Error> {
|
|
||||||
match self.next()? {
|
|
||||||
b'i' => self.int(),
|
|
||||||
b'l' => self.list(),
|
|
||||||
b'd' => self.dict(),
|
|
||||||
b'0'..=b'9' => self.string(),
|
|
||||||
found => Err(UnexpectedByte { found }),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extract_digits(&mut self) -> Result<&'buffer [u8], Error> {
|
|
||||||
let start = self.index;
|
|
||||||
|
|
||||||
while let b'0'..=b'9' = self.next()? {
|
|
||||||
self.advance()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(&self.buffer[start..self.index])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_digits(digits: &[u8]) -> Result<u64, Error> {
|
|
||||||
if digits.is_empty() {
|
|
||||||
return Err(EmptyInteger);
|
|
||||||
}
|
|
||||||
|
|
||||||
if digits == b"0" {
|
|
||||||
return Ok(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut i = 0;
|
|
||||||
|
|
||||||
for digit in digits {
|
|
||||||
let value: u64 = (digit - b'0').into();
|
|
||||||
|
|
||||||
if value == 0 && i == 0 {
|
|
||||||
return Err(LeadingZero);
|
|
||||||
}
|
|
||||||
|
|
||||||
i = i * 10 + value;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn int(&mut self) -> Result<Value<'buffer>, Error> {
|
|
||||||
self.expect(b'i')?;
|
|
||||||
|
|
||||||
let start = self.index;
|
|
||||||
|
|
||||||
let negative = self.accept(b'-')?;
|
|
||||||
|
|
||||||
let digits = self.extract_digits()?;
|
|
||||||
|
|
||||||
let end = self.index;
|
|
||||||
|
|
||||||
self.expect(b'e')?;
|
|
||||||
|
|
||||||
let value = Self::parse_digits(digits)?;
|
|
||||||
|
|
||||||
if value == 0 && negative {
|
|
||||||
return Err(NegativeZero);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Value::Int(
|
|
||||||
str::from_utf8(&self.buffer[start..end]).unwrap(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn list(&mut self) -> Result<Value<'buffer>, Error> {
|
|
||||||
self.expect(b'l')?;
|
|
||||||
|
|
||||||
let mut values = Vec::new();
|
|
||||||
|
|
||||||
while self.next()? != b'e' {
|
|
||||||
values.push(self.value()?);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.expect(b'e')?;
|
|
||||||
|
|
||||||
Ok(Value::List(values))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dict(&mut self) -> Result<Value<'buffer>, Error> {
|
|
||||||
self.expect(b'd')?;
|
|
||||||
|
|
||||||
let mut values: Vec<(&[u8], Value)> = Vec::new();
|
|
||||||
|
|
||||||
while self.next()? != b'e' {
|
|
||||||
let key = self.key()?;
|
|
||||||
|
|
||||||
if let Some((last_key, _)) = values.last() {
|
|
||||||
match key.cmp(last_key) {
|
|
||||||
Ordering::Equal => return Err(DuplicateKey),
|
|
||||||
Ordering::Less => return Err(UnsortedKey),
|
|
||||||
Ordering::Greater => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let value = self.value()?;
|
|
||||||
|
|
||||||
values.push((key, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
self.expect(b'e')?;
|
|
||||||
|
|
||||||
Ok(Value::Dict(values))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn string(&mut self) -> Result<Value<'buffer>, Error> {
|
|
||||||
Ok(Value::Str(self.key()?))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn key(&mut self) -> Result<&'buffer [u8], Error> {
|
|
||||||
let digits = self.extract_digits()?;
|
|
||||||
|
|
||||||
self.expect(b':')?;
|
|
||||||
|
|
||||||
let len = Self::parse_digits(digits)?;
|
|
||||||
|
|
||||||
let start = self.index;
|
|
||||||
for _ in 0..len {
|
|
||||||
self.advance()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(&self.buffer[start..self.index])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next(&self) -> Result<u8, Error> {
|
|
||||||
self
|
|
||||||
.buffer
|
|
||||||
.get(self.index)
|
|
||||||
.cloned()
|
|
||||||
.ok_or(UnexpectedEndOfBuffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn advance(&mut self) -> Result<(), Error> {
|
|
||||||
if self.index == self.buffer.len() {
|
|
||||||
Err(UnexpectedEndOfBuffer)
|
|
||||||
} else {
|
|
||||||
self.index += 1;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expect(&mut self, expected: u8) -> Result<(), Error> {
|
|
||||||
let found = self.next()?;
|
|
||||||
|
|
||||||
if found != expected {
|
|
||||||
return Err(UnexpectedByte { found });
|
|
||||||
}
|
|
||||||
|
|
||||||
self.advance()?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn accept(&mut self, acceptable: u8) -> Result<bool, Error> {
|
|
||||||
if self.next()? == acceptable {
|
|
||||||
self.advance()?;
|
|
||||||
Ok(true)
|
|
||||||
} else {
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
fn err(input: impl AsRef<[u8]>, expected: Error) {
|
|
||||||
let buffer = input.as_ref();
|
|
||||||
let text = String::from_utf8_lossy(buffer);
|
|
||||||
match Parser::parse(buffer) {
|
|
||||||
Ok(_) => panic!(
|
|
||||||
"Input `{}` passed validation, expected: {:?}",
|
|
||||||
text, expected,
|
|
||||||
),
|
|
||||||
Err(error) => assert_eq!(error, expected, "Unexpected error for input `{}`", text),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ok(input: impl AsRef<[u8]>) {
|
|
||||||
let buffer = input.as_ref();
|
|
||||||
match Value::decode(buffer) {
|
|
||||||
Err(_) => {
|
|
||||||
panic!(
|
|
||||||
"Input failed to validate: `{}`",
|
|
||||||
String::from_utf8_lossy(buffer)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(value) => {
|
|
||||||
let round_trip = value.encode();
|
|
||||||
assert_eq!(round_trip, buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn misc() {
|
|
||||||
err("", UnexpectedEndOfBuffer);
|
|
||||||
err("i20efoo", TrailingData { start: 4 });
|
|
||||||
err("defoo", TrailingData { start: 2 });
|
|
||||||
err("lefoo", TrailingData { start: 2 });
|
|
||||||
err("1:afoo", TrailingData { start: 3 });
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn int() {
|
|
||||||
err("ie", EmptyInteger);
|
|
||||||
err("i-0e", NegativeZero);
|
|
||||||
err("i00e", LeadingZero);
|
|
||||||
err("iae", UnexpectedByte { found: b'a' });
|
|
||||||
ok("i0e");
|
|
||||||
ok("i-100e");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn list() {
|
|
||||||
ok("le");
|
|
||||||
ok("llelelee");
|
|
||||||
ok("li20ee");
|
|
||||||
ok("li20edelee");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn dict() {
|
|
||||||
ok("de");
|
|
||||||
ok("d0:0:e");
|
|
||||||
err("di0elee", UnexpectedByte { found: b'i' });
|
|
||||||
err("d0:i0ei0ei0ee", UnexpectedByte { found: b'i' });
|
|
||||||
err("d0:e", UnexpectedByte { found: b'e' });
|
|
||||||
err("d0:de0:dee", DuplicateKey);
|
|
||||||
err("d1:ade0:dee", UnsortedKey);
|
|
||||||
err("d1:ade0:dee", UnsortedKey);
|
|
||||||
ok("d1:ade1:bde1:cdee");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn string() {
|
|
||||||
ok("0:");
|
|
||||||
ok("5:hello");
|
|
||||||
err("1:", UnexpectedEndOfBuffer);
|
|
||||||
err("2:a", UnexpectedEndOfBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
// stdlib types
|
// stdlib types
|
||||||
pub(crate) use std::{
|
pub(crate) use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
cmp::{Ordering, Reverse},
|
cmp::Reverse,
|
||||||
collections::{BTreeMap, BTreeSet, HashMap},
|
collections::{BTreeMap, BTreeSet, HashMap},
|
||||||
convert::{Infallible, TryInto},
|
convert::{Infallible, TryInto},
|
||||||
env,
|
env,
|
||||||
|
@ -20,11 +20,13 @@ pub(crate) use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
// dependencies
|
// dependencies
|
||||||
|
pub(crate) use bendy::{decoding::FromBencode, encoding::ToBencode, value::Value};
|
||||||
pub(crate) use chrono::{TimeZone, Utc};
|
pub(crate) use chrono::{TimeZone, Utc};
|
||||||
pub(crate) use globset::{Glob, GlobMatcher};
|
pub(crate) use globset::{Glob, GlobMatcher};
|
||||||
pub(crate) use libc::EXIT_FAILURE;
|
pub(crate) use libc::EXIT_FAILURE;
|
||||||
pub(crate) use regex::{Regex, RegexSet};
|
pub(crate) use regex::{Regex, RegexSet};
|
||||||
pub(crate) use serde::{Deserialize, Serialize};
|
pub(crate) use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
pub(crate) use serde_with::skip_serializing_none;
|
||||||
pub(crate) use sha1::Sha1;
|
pub(crate) use sha1::Sha1;
|
||||||
pub(crate) use snafu::{ResultExt, Snafu};
|
pub(crate) use snafu::{ResultExt, Snafu};
|
||||||
pub(crate) use static_assertions::const_assert;
|
pub(crate) use static_assertions::const_assert;
|
||||||
|
@ -37,7 +39,7 @@ pub(crate) use url::Url;
|
||||||
pub(crate) use walkdir::WalkDir;
|
pub(crate) use walkdir::WalkDir;
|
||||||
|
|
||||||
// modules
|
// modules
|
||||||
pub(crate) use crate::{bencode, consts, error, use_color};
|
pub(crate) use crate::{consts, error, inner, use_color};
|
||||||
|
|
||||||
// traits
|
// traits
|
||||||
pub(crate) use crate::{
|
pub(crate) use crate::{
|
||||||
|
@ -70,3 +72,6 @@ pub(crate) use crate::testing;
|
||||||
// test structs and enums
|
// test structs and enums
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) use crate::{capture::Capture, test_env::TestEnv, test_env_builder::TestEnvBuilder};
|
pub(crate) use crate::{capture::Capture, test_env::TestEnv, test_env_builder::TestEnvBuilder};
|
||||||
|
|
||||||
|
// type aliases
|
||||||
|
pub(crate) type Result<T, E = Error> = std::result::Result<T, E>;
|
||||||
|
|
|
@ -11,11 +11,11 @@ pub(crate) enum Error {
|
||||||
AnnounceUrlParse { source: url::ParseError },
|
AnnounceUrlParse { source: url::ParseError },
|
||||||
#[snafu(display("Failed to deserialize torrent metainfo from `{}`: {}", path.display(), source))]
|
#[snafu(display("Failed to deserialize torrent metainfo from `{}`: {}", path.display(), source))]
|
||||||
MetainfoLoad {
|
MetainfoLoad {
|
||||||
source: serde_bencode::Error,
|
source: bendy::serde::Error,
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
},
|
},
|
||||||
#[snafu(display("Failed to serialize torrent metainfo: {}", source))]
|
#[snafu(display("Failed to serialize torrent metainfo: {}", source))]
|
||||||
MetainfoSerialize { source: serde_bencode::Error },
|
MetainfoSerialize { source: bendy::serde::Error },
|
||||||
#[snafu(display("Failed to parse byte count `{}`: {}", text, source))]
|
#[snafu(display("Failed to parse byte count `{}`: {}", text, source))]
|
||||||
ByteParse {
|
ByteParse {
|
||||||
text: String,
|
text: String,
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
|
#[skip_serializing_none]
|
||||||
#[derive(Deserialize, Serialize, Debug, PartialEq)]
|
#[derive(Deserialize, Serialize, Debug, PartialEq)]
|
||||||
pub(crate) struct FileInfo {
|
pub(crate) struct FileInfo {
|
||||||
pub(crate) length: u64,
|
pub(crate) length: u64,
|
||||||
pub(crate) md5sum: Option<String>,
|
|
||||||
pub(crate) path: FilePath,
|
pub(crate) path: FilePath,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none", default, with = "inner")]
|
||||||
|
pub(crate) md5sum: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[skip_serializing_none]
|
||||||
|
#[derive(Deserialize, Serialize, Debug, PartialEq)]
|
||||||
pub(crate) struct Info {
|
pub(crate) struct Info {
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none", default, with = "inner")]
|
||||||
pub(crate) private: Option<u8>,
|
pub(crate) private: Option<u8>,
|
||||||
#[serde(rename = "piece length")]
|
#[serde(rename = "piece length")]
|
||||||
pub(crate) piece_length: u32,
|
pub(crate) piece_length: u32,
|
||||||
pub(crate) name: String,
|
pub(crate) name: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none", default, with = "inner")]
|
||||||
pub(crate) source: Option<String>,
|
pub(crate) source: Option<String>,
|
||||||
#[serde(with = "serde_bytes")]
|
#[serde(with = "serde_bytes")]
|
||||||
pub(crate) pieces: Vec<u8>,
|
pub(crate) pieces: Vec<u8>,
|
||||||
|
|
52
src/inner.rs
Normal file
52
src/inner.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
use crate::common::*;
|
||||||
|
|
||||||
|
pub(crate) fn serialize<T, S>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
T: Serialize,
|
||||||
|
{
|
||||||
|
value.as_ref().unwrap().serialize(serializer)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn deserialize<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
T: Deserialize<'de>,
|
||||||
|
{
|
||||||
|
Ok(Some(T::deserialize(deserializer)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
|
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
fn case<T>(value: T, expected: impl AsRef<[u8]>)
|
||||||
|
where
|
||||||
|
T: Serialize + DeserializeOwned + PartialEq + Debug,
|
||||||
|
{
|
||||||
|
let serialized = bendy::serde::ser::to_bytes(&value).unwrap();
|
||||||
|
assert_eq!(serialized, expected.as_ref());
|
||||||
|
|
||||||
|
let deserialized = bendy::serde::de::from_bytes(&serialized).unwrap();
|
||||||
|
assert_eq!(value, deserialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize() {
|
||||||
|
#[derive(Deserialize, Serialize, Debug, PartialEq)]
|
||||||
|
struct Foo {
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none", default, with = "super")]
|
||||||
|
pub(crate) bar: Option<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let none = Foo { bar: None };
|
||||||
|
case(none, b"de");
|
||||||
|
|
||||||
|
let some = Foo { bar: Some(1) };
|
||||||
|
case(some, b"d3:bari1ee");
|
||||||
|
}
|
||||||
|
}
|
|
@ -54,7 +54,6 @@ mod test_env_builder;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod capture;
|
mod capture;
|
||||||
|
|
||||||
mod bencode;
|
|
||||||
mod bytes;
|
mod bytes;
|
||||||
mod common;
|
mod common;
|
||||||
mod consts;
|
mod consts;
|
||||||
|
@ -65,6 +64,7 @@ mod file_path;
|
||||||
mod files;
|
mod files;
|
||||||
mod hasher;
|
mod hasher;
|
||||||
mod info;
|
mod info;
|
||||||
|
mod inner;
|
||||||
mod into_u64;
|
mod into_u64;
|
||||||
mod into_usize;
|
mod into_usize;
|
||||||
mod lint;
|
mod lint;
|
||||||
|
|
|
@ -1,21 +1,28 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[skip_serializing_none]
|
||||||
|
#[derive(Deserialize, Serialize, Debug, PartialEq)]
|
||||||
pub(crate) struct Metainfo {
|
pub(crate) struct Metainfo {
|
||||||
pub(crate) announce: String,
|
pub(crate) announce: String,
|
||||||
#[serde(rename = "announce-list")]
|
#[serde(rename = "announce-list")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none", default, with = "inner")]
|
||||||
pub(crate) announce_list: Option<Vec<Vec<String>>>,
|
pub(crate) announce_list: Option<Vec<Vec<String>>>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none", default, with = "inner")]
|
||||||
pub(crate) comment: Option<String>,
|
pub(crate) comment: Option<String>,
|
||||||
#[serde(rename = "created by")]
|
#[serde(rename = "created by")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none", default, with = "inner")]
|
||||||
pub(crate) created_by: Option<String>,
|
pub(crate) created_by: Option<String>,
|
||||||
#[serde(rename = "creation date")]
|
#[serde(rename = "creation date")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none", default, with = "inner")]
|
||||||
pub(crate) creation_date: Option<u64>,
|
pub(crate) creation_date: Option<u64>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none", default, with = "inner")]
|
||||||
pub(crate) encoding: Option<String>,
|
pub(crate) encoding: Option<String>,
|
||||||
pub(crate) info: Info,
|
pub(crate) info: Info,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Metainfo {
|
impl Metainfo {
|
||||||
pub(crate) fn _load(path: impl AsRef<Path>) -> Result<Metainfo, Error> {
|
#[cfg(test)]
|
||||||
|
pub(crate) fn load(path: impl AsRef<Path>) -> Result<Metainfo, Error> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
let bytes = fs::read(path).context(error::Filesystem { path })?;
|
let bytes = fs::read(path).context(error::Filesystem { path })?;
|
||||||
Self::deserialize(path, &bytes)
|
Self::deserialize(path, &bytes)
|
||||||
|
@ -24,17 +31,45 @@ impl Metainfo {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) fn dump(&self, path: impl AsRef<Path>) -> Result<(), Error> {
|
pub(crate) fn dump(&self, path: impl AsRef<Path>) -> Result<(), Error> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
let bytes = serde_bencode::ser::to_bytes(&self).context(error::MetainfoSerialize)?;
|
let bendy = bendy::serde::ser::to_bytes(&self).context(error::MetainfoSerialize)?;
|
||||||
fs::write(path, &bytes).context(error::Filesystem { path })?;
|
let serde_bencode = serde_bencode::ser::to_bytes(&self).unwrap();
|
||||||
|
if bendy != serde_bencode {
|
||||||
|
panic!(
|
||||||
|
"Serialize bendy != serde_bencode:\n{}\n{}",
|
||||||
|
String::from_utf8_lossy(&bendy),
|
||||||
|
String::from_utf8_lossy(&serde_bencode)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
fs::write(path, &bendy).context(error::Filesystem { path })?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn deserialize(path: impl AsRef<Path>, bytes: &[u8]) -> Result<Metainfo, Error> {
|
pub(crate) fn deserialize(path: impl AsRef<Path>, bytes: &[u8]) -> Result<Metainfo, Error> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
serde_bencode::de::from_bytes(&bytes).context(error::MetainfoLoad { path })
|
let bendy = bendy::serde::de::from_bytes(&bytes).context(error::MetainfoLoad { path })?;
|
||||||
|
let serde_bencode = serde_bencode::de::from_bytes(&bytes).unwrap();
|
||||||
|
assert_eq!(bendy, serde_bencode);
|
||||||
|
Ok(bendy)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn serialize(&self) -> Result<Vec<u8>, Error> {
|
pub(crate) fn serialize(&self) -> Result<Vec<u8>, Error> {
|
||||||
serde_bencode::ser::to_bytes(&self).context(error::MetainfoSerialize)
|
let bendy = bendy::serde::ser::to_bytes(&self).context(error::MetainfoSerialize)?;
|
||||||
|
let serde_bencode = serde_bencode::ser::to_bytes(&self).unwrap();
|
||||||
|
if bendy != serde_bencode {
|
||||||
|
panic!(
|
||||||
|
"Serialize bendy != serde_bencode:\n{}\n{}",
|
||||||
|
String::from_utf8_lossy(&bendy),
|
||||||
|
String::from_utf8_lossy(&serde_bencode)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(bendy)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) fn from_bytes(bytes: &[u8]) -> Metainfo {
|
||||||
|
let bendy = bendy::serde::de::from_bytes(bytes).unwrap();
|
||||||
|
let serde_bencode = serde_bencode::de::from_bytes(bytes).unwrap();
|
||||||
|
assert_eq!(bendy, serde_bencode);
|
||||||
|
bendy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
13
src/mode.rs
13
src/mode.rs
|
@ -1,10 +1,17 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, PartialEq)]
|
#[skip_serializing_none]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
|
#[derive(Deserialize, Serialize, Debug, PartialEq)]
|
||||||
pub(crate) enum Mode {
|
pub(crate) enum Mode {
|
||||||
Single { length: u64, md5sum: Option<String> },
|
Single {
|
||||||
Multiple { files: Vec<FileInfo> },
|
length: u64,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none", default, with = "inner")]
|
||||||
|
md5sum: Option<String>,
|
||||||
|
},
|
||||||
|
Multiple {
|
||||||
|
files: Vec<FileInfo>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mode {
|
impl Mode {
|
||||||
|
|
|
@ -321,8 +321,8 @@ mod tests {
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let bytes = fs::read(torrent).unwrap();
|
||||||
let value = bencode::Value::decode(&bytes).unwrap();
|
let value = Value::from_bencode(&bytes).unwrap();
|
||||||
assert!(matches!(value, bencode::Value::Dict(_)));
|
assert!(matches!(value, Value::Dict(_)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -331,8 +331,7 @@ mod tests {
|
||||||
fs::write(env.resolve("foo"), "").unwrap();
|
fs::write(env.resolve("foo"), "").unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(metainfo.info.private, None);
|
assert_eq!(metainfo.info.private, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,8 +341,7 @@ mod tests {
|
||||||
fs::write(env.resolve("foo"), "").unwrap();
|
fs::write(env.resolve("foo"), "").unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(metainfo.info.private, Some(1));
|
assert_eq!(metainfo.info.private, Some(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,8 +358,7 @@ mod tests {
|
||||||
fs::write(env.resolve("foo"), "").unwrap();
|
fs::write(env.resolve("foo"), "").unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(metainfo.announce, "http://bar/");
|
assert_eq!(metainfo.announce, "http://bar/");
|
||||||
assert!(metainfo.announce_list.is_none());
|
assert!(metainfo.announce_list.is_none());
|
||||||
}
|
}
|
||||||
|
@ -377,8 +374,7 @@ mod tests {
|
||||||
fs::write(env.resolve("foo"), "").unwrap();
|
fs::write(env.resolve("foo"), "").unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
metainfo.announce,
|
metainfo.announce,
|
||||||
"udp://tracker.opentrackr.org:1337/announce"
|
"udp://tracker.opentrackr.org:1337/announce"
|
||||||
|
@ -392,8 +388,7 @@ mod tests {
|
||||||
fs::write(env.resolve("foo"), "").unwrap();
|
fs::write(env.resolve("foo"), "").unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(metainfo.announce, "wss://tracker.btorrent.xyz/");
|
assert_eq!(metainfo.announce, "wss://tracker.btorrent.xyz/");
|
||||||
assert!(metainfo.announce_list.is_none());
|
assert!(metainfo.announce_list.is_none());
|
||||||
}
|
}
|
||||||
|
@ -411,8 +406,7 @@ mod tests {
|
||||||
fs::write(env.resolve("foo"), "").unwrap();
|
fs::write(env.resolve("foo"), "").unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(metainfo.announce, "http://bar/");
|
assert_eq!(metainfo.announce, "http://bar/");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
metainfo.announce_list,
|
metainfo.announce_list,
|
||||||
|
@ -435,8 +429,7 @@ mod tests {
|
||||||
fs::write(env.resolve("foo"), "").unwrap();
|
fs::write(env.resolve("foo"), "").unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(metainfo.announce, "http://bar/");
|
assert_eq!(metainfo.announce, "http://bar/");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
metainfo.announce_list,
|
metainfo.announce_list,
|
||||||
|
@ -453,8 +446,7 @@ mod tests {
|
||||||
fs::write(env.resolve("foo"), "").unwrap();
|
fs::write(env.resolve("foo"), "").unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(metainfo.comment, None);
|
assert_eq!(metainfo.comment, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -471,8 +463,7 @@ mod tests {
|
||||||
fs::write(env.resolve("foo"), "").unwrap();
|
fs::write(env.resolve("foo"), "").unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(metainfo.comment.unwrap(), "Hello, world!");
|
assert_eq!(metainfo.comment.unwrap(), "Hello, world!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,8 +473,7 @@ mod tests {
|
||||||
fs::write(env.resolve("foo"), "").unwrap();
|
fs::write(env.resolve("foo"), "").unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(metainfo.info.piece_length, 16 * 2u32.pow(10));
|
assert_eq!(metainfo.info.piece_length, 16 * 2u32.pow(10));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -500,8 +490,7 @@ mod tests {
|
||||||
fs::write(env.resolve("foo"), "").unwrap();
|
fs::write(env.resolve("foo"), "").unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(metainfo.info.piece_length, 64 * 1024);
|
assert_eq!(metainfo.info.piece_length, 64 * 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -518,8 +507,7 @@ mod tests {
|
||||||
fs::write(env.resolve("foo"), "").unwrap();
|
fs::write(env.resolve("foo"), "").unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(metainfo.info.piece_length, 512 * 1024);
|
assert_eq!(metainfo.info.piece_length, 512 * 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -536,8 +524,7 @@ mod tests {
|
||||||
fs::write(env.resolve("foo"), "").unwrap();
|
fs::write(env.resolve("foo"), "").unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(metainfo.info.name, "foo");
|
assert_eq!(metainfo.info.name, "foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -556,8 +543,7 @@ mod tests {
|
||||||
fs::write(dir.join("bar"), "").unwrap();
|
fs::write(dir.join("bar"), "").unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = dir.join("bar.torrent");
|
let torrent = dir.join("bar.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(metainfo.info.name, "bar");
|
assert_eq!(metainfo.info.name, "bar");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -574,8 +560,7 @@ mod tests {
|
||||||
fs::write(env.resolve("foo"), "").unwrap();
|
fs::write(env.resolve("foo"), "").unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("x.torrent");
|
let torrent = env.resolve("x.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
Metainfo::load(torrent).unwrap();
|
||||||
serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -584,8 +569,7 @@ mod tests {
|
||||||
fs::write(env.resolve("foo"), "").unwrap();
|
fs::write(env.resolve("foo"), "").unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(metainfo.created_by.unwrap(), consts::CREATED_BY_DEFAULT);
|
assert_eq!(metainfo.created_by.unwrap(), consts::CREATED_BY_DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -601,8 +585,7 @@ mod tests {
|
||||||
fs::write(env.resolve("foo"), "").unwrap();
|
fs::write(env.resolve("foo"), "").unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(metainfo.created_by, None);
|
assert_eq!(metainfo.created_by, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -612,8 +595,7 @@ mod tests {
|
||||||
fs::write(env.resolve("foo"), "").unwrap();
|
fs::write(env.resolve("foo"), "").unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(metainfo.encoding, Some("UTF-8".into()));
|
assert_eq!(metainfo.encoding, Some("UTF-8".into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -627,8 +609,7 @@ mod tests {
|
||||||
.as_secs();
|
.as_secs();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert!(metainfo.creation_date.unwrap() < now + 10);
|
assert!(metainfo.creation_date.unwrap() < now + 10);
|
||||||
assert!(metainfo.creation_date.unwrap() > now - 10);
|
assert!(metainfo.creation_date.unwrap() > now - 10);
|
||||||
}
|
}
|
||||||
|
@ -645,8 +626,7 @@ mod tests {
|
||||||
fs::write(env.resolve("foo"), "").unwrap();
|
fs::write(env.resolve("foo"), "").unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(metainfo.creation_date, None);
|
assert_eq!(metainfo.creation_date, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -657,8 +637,7 @@ mod tests {
|
||||||
fs::write(env.resolve("foo"), contents).unwrap();
|
fs::write(env.resolve("foo"), contents).unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(metainfo.info.pieces, Sha1::from(contents).digest().bytes());
|
assert_eq!(metainfo.info.pieces, Sha1::from(contents).digest().bytes());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
metainfo.info.mode,
|
metainfo.info.mode,
|
||||||
|
@ -685,8 +664,7 @@ mod tests {
|
||||||
fs::write(env.resolve("foo"), contents).unwrap();
|
fs::write(env.resolve("foo"), contents).unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
let pieces = Sha1::from("b")
|
let pieces = Sha1::from("b")
|
||||||
.digest()
|
.digest()
|
||||||
.bytes()
|
.bytes()
|
||||||
|
@ -713,8 +691,7 @@ mod tests {
|
||||||
fs::write(env.resolve("foo"), contents).unwrap();
|
fs::write(env.resolve("foo"), contents).unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(metainfo.info.pieces.len(), 0);
|
assert_eq!(metainfo.info.pieces.len(), 0);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
metainfo.info.mode,
|
metainfo.info.mode,
|
||||||
|
@ -732,8 +709,7 @@ mod tests {
|
||||||
fs::create_dir(&dir).unwrap();
|
fs::create_dir(&dir).unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(metainfo.info.pieces.len(), 0);
|
assert_eq!(metainfo.info.pieces.len(), 0);
|
||||||
assert_eq!(metainfo.info.mode, Mode::Multiple { files: Vec::new() })
|
assert_eq!(metainfo.info.mode, Mode::Multiple { files: Vec::new() })
|
||||||
}
|
}
|
||||||
|
@ -748,8 +724,7 @@ mod tests {
|
||||||
fs::write(file, contents).unwrap();
|
fs::write(file, contents).unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(metainfo.info.pieces, Sha1::from(contents).digest().bytes());
|
assert_eq!(metainfo.info.pieces, Sha1::from(contents).digest().bytes());
|
||||||
match metainfo.info.mode {
|
match metainfo.info.mode {
|
||||||
Mode::Multiple { files } => {
|
Mode::Multiple { files } => {
|
||||||
|
@ -776,8 +751,7 @@ mod tests {
|
||||||
fs::write(file, contents).unwrap();
|
fs::write(file, contents).unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(metainfo.info.pieces, Sha1::from(contents).digest().bytes());
|
assert_eq!(metainfo.info.pieces, Sha1::from(contents).digest().bytes());
|
||||||
match metainfo.info.mode {
|
match metainfo.info.mode {
|
||||||
Mode::Multiple { files } => {
|
Mode::Multiple { files } => {
|
||||||
|
@ -804,8 +778,7 @@ mod tests {
|
||||||
fs::write(dir.join("h"), "hij").unwrap();
|
fs::write(dir.join("h"), "hij").unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
metainfo.info.pieces,
|
metainfo.info.pieces,
|
||||||
Sha1::from("abchijxyz").digest().bytes()
|
Sha1::from("abchijxyz").digest().bytes()
|
||||||
|
@ -1028,8 +1001,7 @@ Content Size 9 bytes
|
||||||
fs::write(env.resolve("foo"), "").unwrap();
|
fs::write(env.resolve("foo"), "").unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let bytes = env.out_bytes();
|
let bytes = env.out_bytes();
|
||||||
let value = bencode::Value::decode(&bytes).unwrap();
|
Metainfo::from_bytes(&bytes);
|
||||||
assert!(matches!(value, bencode::Value::Dict(_)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1052,8 +1024,8 @@ Content Size 9 bytes
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let bytes = fs::read(torrent).unwrap();
|
||||||
let value = bencode::Value::decode(&bytes).unwrap();
|
let value = Value::from_bencode(&bytes).unwrap();
|
||||||
assert!(matches!(value, bencode::Value::Dict(_)));
|
assert!(matches!(value, Value::Dict(_)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1065,8 +1037,7 @@ Content Size 9 bytes
|
||||||
fs::write(dir.join("Desktop.ini"), "abc").unwrap();
|
fs::write(dir.join("Desktop.ini"), "abc").unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
metainfo.info.mode,
|
metainfo.info.mode,
|
||||||
Mode::Multiple { files } if files.is_empty()
|
Mode::Multiple { files } if files.is_empty()
|
||||||
|
@ -1089,8 +1060,7 @@ Content Size 9 bytes
|
||||||
fs::write(dir.join("Desktop.ini"), "abc").unwrap();
|
fs::write(dir.join("Desktop.ini"), "abc").unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
metainfo.info.mode,
|
metainfo.info.mode,
|
||||||
Mode::Multiple { files } if files.len() == 2
|
Mode::Multiple { files } if files.len() == 2
|
||||||
|
@ -1126,8 +1096,7 @@ Content Size 9 bytes
|
||||||
}
|
}
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
metainfo.info.mode,
|
metainfo.info.mode,
|
||||||
Mode::Multiple { files } if files.len() == 0
|
Mode::Multiple { files } if files.len() == 0
|
||||||
|
@ -1149,8 +1118,7 @@ Content Size 9 bytes
|
||||||
fs::write(dir.join(".hidden"), "abc").unwrap();
|
fs::write(dir.join(".hidden"), "abc").unwrap();
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
metainfo.info.mode,
|
metainfo.info.mode,
|
||||||
Mode::Multiple { files } if files.len() == 1
|
Mode::Multiple { files } if files.len() == 1
|
||||||
|
@ -1194,8 +1162,7 @@ Content Size 9 bytes
|
||||||
populate_symlinks(&env);
|
populate_symlinks(&env);
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
metainfo.info.mode,
|
metainfo.info.mode,
|
||||||
Mode::Multiple { files } if files.is_empty()
|
Mode::Multiple { files } if files.is_empty()
|
||||||
|
@ -1217,8 +1184,7 @@ Content Size 9 bytes
|
||||||
populate_symlinks(&env);
|
populate_symlinks(&env);
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_eq!(metainfo.info.pieces, Sha1::from("barbaz").digest().bytes());
|
assert_eq!(metainfo.info.pieces, Sha1::from("barbaz").digest().bytes());
|
||||||
match metainfo.info.mode {
|
match metainfo.info.mode {
|
||||||
Mode::Multiple { files } => {
|
Mode::Multiple { files } => {
|
||||||
|
@ -1266,8 +1232,7 @@ Content Size 9 bytes
|
||||||
env.create_file("foo/.bar/baz", "baz");
|
env.create_file("foo/.bar/baz", "baz");
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
metainfo.info.mode,
|
metainfo.info.mode,
|
||||||
Mode::Multiple { files } if files.is_empty()
|
Mode::Multiple { files } if files.is_empty()
|
||||||
|
@ -1301,8 +1266,7 @@ Content Size 9 bytes
|
||||||
}
|
}
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
metainfo.info.mode,
|
metainfo.info.mode,
|
||||||
Mode::Multiple { files } if files.is_empty()
|
Mode::Multiple { files } if files.is_empty()
|
||||||
|
@ -1319,8 +1283,7 @@ Content Size 9 bytes
|
||||||
env.create_file("foo/c", "c");
|
env.create_file("foo/c", "c");
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
metainfo.info.mode,
|
metainfo.info.mode,
|
||||||
Mode::Multiple { files } if files.len() == 2
|
Mode::Multiple { files } if files.len() == 2
|
||||||
|
@ -1337,8 +1300,7 @@ Content Size 9 bytes
|
||||||
env.create_file("foo/c", "c");
|
env.create_file("foo/c", "c");
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
metainfo.info.mode,
|
metainfo.info.mode,
|
||||||
Mode::Multiple { files } if files.len() == 3
|
Mode::Multiple { files } if files.len() == 3
|
||||||
|
@ -1362,8 +1324,7 @@ Content Size 9 bytes
|
||||||
env.create_file("foo/c", "c");
|
env.create_file("foo/c", "c");
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
metainfo.info.mode,
|
metainfo.info.mode,
|
||||||
Mode::Multiple { files } if files.len() == 2
|
Mode::Multiple { files } if files.len() == 2
|
||||||
|
@ -1380,8 +1341,7 @@ Content Size 9 bytes
|
||||||
env.create_file("foo/c", "c");
|
env.create_file("foo/c", "c");
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
metainfo.info.mode,
|
metainfo.info.mode,
|
||||||
Mode::Multiple { files } if files.is_empty()
|
Mode::Multiple { files } if files.is_empty()
|
||||||
|
@ -1409,8 +1369,7 @@ Content Size 9 bytes
|
||||||
env.create_file("foo/c", "c");
|
env.create_file("foo/c", "c");
|
||||||
env.run().unwrap();
|
env.run().unwrap();
|
||||||
let torrent = env.resolve("foo.torrent");
|
let torrent = env.resolve("foo.torrent");
|
||||||
let bytes = fs::read(torrent).unwrap();
|
let metainfo = Metainfo::load(torrent).unwrap();
|
||||||
let metainfo = serde_bencode::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
metainfo.info.mode,
|
metainfo.info.mode,
|
||||||
Mode::Multiple { files } if files.len() == 1
|
Mode::Multiple { files } if files.len() == 1
|
||||||
|
|
|
@ -160,55 +160,106 @@ impl Extractor {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let value = if let Ok(value) = bencode::Value::decode(&contents) {
|
if let Ok(value) = Value::from_bencode(&contents) {
|
||||||
value
|
self.extract(&value);
|
||||||
|
if self.print {
|
||||||
|
eprintln!("{}:\n{}", path.display(), Self::pretty_print(&value));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.bencode_decode_errors += 1;
|
self.bencode_decode_errors += 1;
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
if self.print {
|
|
||||||
eprintln!("{}:\n{}", path.display(), value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.extract(&value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract(&mut self, value: &bencode::Value) {
|
fn extract(&mut self, value: &Value) {
|
||||||
use bencode::Value::*;
|
|
||||||
|
|
||||||
let matches = self.regex_set.matches(&self.current_path);
|
let matches = self.regex_set.matches(&self.current_path);
|
||||||
|
|
||||||
for i in matches.iter() {
|
for i in matches.iter() {
|
||||||
let pattern = &self.regex_set.patterns()[i];
|
let pattern = &self.regex_set.patterns()[i];
|
||||||
if let Some(values) = self.values.get_mut(pattern) {
|
if let Some(values) = self.values.get_mut(pattern) {
|
||||||
values.push(value.to_string());
|
values.push(Self::pretty_print(value));
|
||||||
} else {
|
} else {
|
||||||
self.values.insert(pattern.clone(), vec![value.to_string()]);
|
self
|
||||||
|
.values
|
||||||
|
.insert(pattern.clone(), vec![Self::pretty_print(value)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let starting_length = self.current_path.len();
|
let starting_length = self.current_path.len();
|
||||||
|
|
||||||
if let Dict(items) = value {
|
match value {
|
||||||
for (key, value) in items {
|
Value::List(list) => {
|
||||||
match String::from_utf8_lossy(key) {
|
if self.current_path.pop().is_some() {
|
||||||
Cow::Borrowed(s) => self.current_path.push_str(s),
|
self.current_path.push('*');
|
||||||
Cow::Owned(s) => self.current_path.push_str(&s),
|
}
|
||||||
|
for value in list {
|
||||||
|
self.extract(value);
|
||||||
}
|
}
|
||||||
self.paths.increment_ref(&self.current_path);
|
|
||||||
self.current_path.push('/');
|
|
||||||
self.extract(value);
|
|
||||||
self.current_path.truncate(starting_length);
|
self.current_path.truncate(starting_length);
|
||||||
}
|
}
|
||||||
} else if let List(values) = value {
|
Value::Dict(dict) => {
|
||||||
if self.current_path.pop().is_some() {
|
for (key, value) in dict {
|
||||||
self.current_path.push('*');
|
match String::from_utf8_lossy(key) {
|
||||||
|
Cow::Borrowed(s) => self.current_path.push_str(s),
|
||||||
|
Cow::Owned(s) => self.current_path.push_str(&s),
|
||||||
|
}
|
||||||
|
self.paths.increment_ref(&self.current_path);
|
||||||
|
self.current_path.push('/');
|
||||||
|
self.extract(value);
|
||||||
|
self.current_path.truncate(starting_length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for value in values {
|
Value::Integer(_) | Value::Bytes(_) => {}
|
||||||
self.extract(value);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pretty_print(value: &Value) -> String {
|
||||||
|
let mut buffer = String::new();
|
||||||
|
Self::pretty_print_inner(value, &mut buffer);
|
||||||
|
buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pretty_print_inner(value: &Value, buffer: &mut String) {
|
||||||
|
match value {
|
||||||
|
Value::List(list) => {
|
||||||
|
buffer.push('[');
|
||||||
|
for (i, value) in list.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
buffer.push_str(", ");
|
||||||
|
}
|
||||||
|
Self::pretty_print_inner(value, buffer);
|
||||||
|
}
|
||||||
|
buffer.push(']');
|
||||||
}
|
}
|
||||||
self.current_path.truncate(starting_length);
|
Value::Dict(dict) => {
|
||||||
|
buffer.push('{');
|
||||||
|
for (i, (key, value)) in dict.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
buffer.push_str(", ");
|
||||||
|
}
|
||||||
|
Self::pretty_print_string(key, buffer);
|
||||||
|
buffer.push_str(": ");
|
||||||
|
Self::pretty_print_inner(value, buffer);
|
||||||
|
}
|
||||||
|
buffer.push('}');
|
||||||
|
}
|
||||||
|
Value::Integer(integer) => buffer.push_str(&integer.to_string()),
|
||||||
|
Value::Bytes(bytes) => {
|
||||||
|
Self::pretty_print_string(bytes, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pretty_print_string(string: &[u8], buffer: &mut String) {
|
||||||
|
if let Ok(text) = str::from_utf8(string) {
|
||||||
|
buffer.push('\"');
|
||||||
|
buffer.push_str(text);
|
||||||
|
buffer.push('\"');
|
||||||
|
} else {
|
||||||
|
buffer.push('<');
|
||||||
|
for byte in string {
|
||||||
|
buffer.push_str(&format!("{:X}", byte));
|
||||||
|
}
|
||||||
|
buffer.push('>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,15 +8,16 @@ pub(crate) struct TorrentSummary {
|
||||||
|
|
||||||
impl TorrentSummary {
|
impl TorrentSummary {
|
||||||
fn new(bytes: &[u8], metainfo: Metainfo) -> Result<Self, Error> {
|
fn new(bytes: &[u8], metainfo: Metainfo) -> Result<Self, Error> {
|
||||||
let value = bencode::Value::decode(&bytes).unwrap();
|
let value = Value::from_bencode(&bytes).unwrap();
|
||||||
|
|
||||||
let infohash = if let bencode::Value::Dict(items) = value {
|
let infohash = if let Value::Dict(items) = value {
|
||||||
let info = items
|
let info = items
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(key, _value)| key == b"info")
|
.find(|pair: &(&Vec<u8>, &Value)| pair.0 == b"info")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.1
|
.1
|
||||||
.encode();
|
.to_bencode()
|
||||||
|
.unwrap();
|
||||||
Sha1::from(info).digest()
|
Sha1::from(info).digest()
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user