refactor and simplify the bencode objects
This commit is contained in:
@@ -2,7 +2,7 @@ use std::collections::BTreeMap;
|
|||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
use std::str::{self, Utf8Error};
|
use std::str::{self, Utf8Error};
|
||||||
|
|
||||||
use bencode::{Bytes, Dict, List, Object};
|
use bencode::{Bytes, Object};
|
||||||
use buffer::Buffer;
|
use buffer::Buffer;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -46,7 +46,7 @@ fn decode_object(buf: &mut Buffer) -> DecodeResult<Object> {
|
|||||||
}
|
}
|
||||||
list.push(decode_object(buf)?);
|
list.push(decode_object(buf)?);
|
||||||
}
|
}
|
||||||
Ok(List::wrap(list))
|
Ok(list.into())
|
||||||
}
|
}
|
||||||
Some(b'd') => {
|
Some(b'd') => {
|
||||||
buf.advance(1);
|
buf.advance(1);
|
||||||
@@ -60,7 +60,7 @@ fn decode_object(buf: &mut Buffer) -> DecodeResult<Object> {
|
|||||||
let val = decode_object(buf)?;
|
let val = decode_object(buf)?;
|
||||||
dict.insert(Bytes(key), val);
|
dict.insert(Bytes(key), val);
|
||||||
}
|
}
|
||||||
Ok(Dict::wrap(dict))
|
Ok(dict.into())
|
||||||
}
|
}
|
||||||
_ => Err(DecodeError),
|
_ => Err(DecodeError),
|
||||||
}
|
}
|
||||||
@@ -84,7 +84,7 @@ fn _decode_int(buf: &mut Buffer, term: u8) -> DecodeResult<i64> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn decode_bytes(buf: &mut Buffer) -> DecodeResult<Object> {
|
fn decode_bytes(buf: &mut Buffer) -> DecodeResult<Object> {
|
||||||
_decode_bytes(buf).map(|bytes| Bytes::wrap(bytes))
|
_decode_bytes(buf).map(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _decode_bytes(buf: &mut Buffer) -> Result<Vec<u8>, DecodeError> {
|
fn _decode_bytes(buf: &mut Buffer) -> Result<Vec<u8>, DecodeError> {
|
||||||
@@ -116,12 +116,14 @@ fn test_int_neg() {
|
|||||||
fn test_bytes() {
|
fn test_bytes() {
|
||||||
let mut buf = Buffer::new(b"5:hello");
|
let mut buf = Buffer::new(b"5:hello");
|
||||||
|
|
||||||
assert_eq!(decode_bytes(&mut buf).unwrap(), Bytes::wrap(b"hello".to_vec()));
|
assert_eq!(decode_bytes(&mut buf).unwrap(), "hello".into());
|
||||||
assert_eq!(buf.pos(), 7);
|
assert_eq!(buf.pos(), 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_list() {
|
fn test_list() {
|
||||||
|
use bencode::List;
|
||||||
|
|
||||||
let mut buf = Buffer::new(b"li1ei2ei3ee");
|
let mut buf = Buffer::new(b"li1ei2ei3ee");
|
||||||
|
|
||||||
let obj = decode_object(&mut buf).unwrap();
|
let obj = decode_object(&mut buf).unwrap();
|
||||||
|
|||||||
@@ -1,10 +1,28 @@
|
|||||||
use std::borrow::Borrow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use bencode::{Dict, Object};
|
use bencode::{Dict, Object};
|
||||||
|
|
||||||
pub fn encode<B>(obj: B) -> Vec<u8> where B: Borrow<Object> {
|
/// Allows the encode function to take something that can be converted into an object,
|
||||||
|
/// or a reference to an object.
|
||||||
|
pub trait Encodable<'a> {
|
||||||
|
fn get_object(self) -> Cow<'a, Object>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Encodable<'a> for T where T: Into<Object> {
|
||||||
|
fn get_object(self) -> Cow<'a, Object> {
|
||||||
|
Cow::Owned(self.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Encodable<'a> for &'a Object {
|
||||||
|
fn get_object(self) -> Cow<'a, Object> {
|
||||||
|
Cow::Borrowed(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode<'a, O>(obj: O) -> Vec<u8> where O: Encodable<'a> {
|
||||||
let mut buff = Vec::new();
|
let mut buff = Vec::new();
|
||||||
encode_object(&mut buff, obj.borrow());
|
encode_object(&mut buff, &obj.get_object());
|
||||||
buff
|
buff
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,35 +63,30 @@ fn encode_dict(buff: &mut Vec<u8>, dict: &Dict) {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_int_pos() {
|
fn test_int_pos() {
|
||||||
assert_eq!(encode(Object::Int(1337)), b"i1337e");
|
assert_eq!(encode(1337), b"i1337e");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_int_neg() {
|
fn test_int_neg() {
|
||||||
assert_eq!(encode(Object::Int(-1337)), b"i-1337e");
|
assert_eq!(encode(-1337), b"i-1337e");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bytes() {
|
fn test_bytes() {
|
||||||
use bencode::Bytes;
|
assert_eq!(encode("hello"), b"5:hello");
|
||||||
|
|
||||||
assert_eq!(encode(Bytes::wrap(b"hello".to_vec())), b"5:hello");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_list() {
|
fn test_list() {
|
||||||
use bencode::List;
|
|
||||||
|
|
||||||
let list = vec![Object::Int(1), Object::Int(2), Object::Int(3)];
|
let list = vec![Object::Int(1), Object::Int(2), Object::Int(3)];
|
||||||
assert_eq!(encode(List::wrap(list)), b"li1ei2ei3ee");
|
assert_eq!(encode(list), b"li1ei2ei3ee");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_dict() {
|
fn test_dict() {
|
||||||
use std::collections::BTreeMap;
|
use bencode::Dict;
|
||||||
use bencode::{Bytes, Dict};
|
|
||||||
|
|
||||||
let mut dict = BTreeMap::new();
|
let mut dict = Dict::new();
|
||||||
dict.insert(Bytes(b"hello".to_vec()), Object::Int(1337));
|
dict.insert("hello", 1337);
|
||||||
assert_eq!(encode(Dict::wrap(dict)), b"d5:helloi1337ee")
|
assert_eq!(encode(dict), b"d5:helloi1337ee")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
mod decode;
|
mod decode;
|
||||||
mod encode;
|
mod encode;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
pub use self::decode::{decode, DecodeError, DecodeResult};
|
pub use self::decode::{decode, DecodeError, DecodeResult};
|
||||||
pub use self::encode::encode;
|
pub use self::encode::{encode, Encodable};
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq)]
|
#[derive(Clone, Eq, PartialEq)]
|
||||||
pub enum Object {
|
pub enum Object {
|
||||||
@@ -91,10 +90,6 @@ impl Bytes {
|
|||||||
Bytes(bytes.into())
|
Bytes(bytes.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wrap<A>(bytes: A) -> Object where A: Into<Vec<u8>> {
|
|
||||||
Object::Bytes(Bytes(bytes.into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn str(&self) -> Option<&str> {
|
pub fn str(&self) -> Option<&str> {
|
||||||
str::from_utf8(&self.0).ok()
|
str::from_utf8(&self.0).ok()
|
||||||
}
|
}
|
||||||
@@ -118,28 +113,6 @@ impl DerefMut for Bytes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait AsBytes {
|
|
||||||
fn as_bytes(&self) -> Cow<Bytes>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsBytes for Bytes {
|
|
||||||
fn as_bytes(&self) -> Cow<Bytes> {
|
|
||||||
Cow::Borrowed(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> AsBytes for &'a str {
|
|
||||||
fn as_bytes(&self) -> Cow<Bytes> {
|
|
||||||
Cow::Owned(Bytes(str::as_bytes(self).to_vec()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> AsBytes for &'a [u8] {
|
|
||||||
fn as_bytes(&self) -> Cow<Bytes> {
|
|
||||||
Cow::Owned(Bytes(self.to_vec()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct List(Vec<Object>);
|
pub struct List(Vec<Object>);
|
||||||
|
|
||||||
@@ -148,8 +121,8 @@ impl List {
|
|||||||
List(Vec::new())
|
List(Vec::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wrap(list: Vec<Object>) -> Object {
|
pub fn push<V>(&mut self, val: V) where V: Into<Object> {
|
||||||
Object::List(List(list))
|
self.0.push(val.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,36 +148,36 @@ impl Dict {
|
|||||||
Dict(BTreeMap::new())
|
Dict(BTreeMap::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wrap(dict: BTreeMap<Bytes, Object>) -> Object {
|
pub fn insert<K, V>(&mut self, key: K, val: V) where K: Into<Bytes>, V: Into<Object> {
|
||||||
Object::Dict(Dict(dict))
|
self.0.insert(key.into(), val.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_object<A>(&self, key: A) -> Option<&Object> where A: AsBytes {
|
pub fn get_object<A>(&self, key: A) -> Option<&Object> where A: Into<Bytes> {
|
||||||
self.0.get(&key.as_bytes())
|
self.0.get(&key.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_int<A>(&self, key: A) -> Option<i64> where A: AsBytes {
|
pub fn get_int<A>(&self, key: A) -> Option<i64> where A: Into<Bytes> {
|
||||||
self.0.get(&key.as_bytes()).and_then(|o| o.as_int())
|
self.0.get(&key.into()).and_then(|o| o.as_int())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_bytes<A>(&self, key: A) -> Option<&Bytes> where A: AsBytes {
|
pub fn get_bytes<A>(&self, key: A) -> Option<&Bytes> where A: Into<Bytes> {
|
||||||
self.0.get(&key.as_bytes()).and_then(|o| o.as_bytes())
|
self.0.get(&key.into()).and_then(|o| o.as_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_str<A>(&self, key: A) -> Option<&str> where A: AsBytes {
|
pub fn get_str<A>(&self, key: A) -> Option<&str> where A: Into<Bytes> {
|
||||||
self.0.get(&key.as_bytes()).and_then(|o| o.as_bytes().and_then(|b| str::from_utf8(b).ok()))
|
self.0.get(&key.into()).and_then(|o| o.as_bytes().and_then(|b| str::from_utf8(b).ok()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_string<A>(&self, key: A) -> Option<String> where A: AsBytes {
|
pub fn get_string<A>(&self, key: A) -> Option<String> where A: Into<Bytes> {
|
||||||
self.get_str(key).map(|s| s.to_string())
|
self.get_str(key).map(|s| s.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_list<A>(&self, key: A) -> Option<&List> where A: AsBytes {
|
pub fn get_list<A>(&self, key: A) -> Option<&List> where A: Into<Bytes> {
|
||||||
self.0.get(&key.as_bytes()).and_then(|o| o.as_list())
|
self.0.get(&key.into()).and_then(|o| o.as_list())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_dict<A>(&self, key: A) -> Option<&Dict> where A: AsBytes {
|
pub fn get_dict<A>(&self, key: A) -> Option<&Dict> where A: Into<Bytes> {
|
||||||
self.0.get(&key.as_bytes()).and_then(|o| o.as_dict())
|
self.0.get(&key.into()).and_then(|o| o.as_dict())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,3 +194,97 @@ impl DerefMut for Dict {
|
|||||||
&mut self.0
|
&mut self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convertion utilities
|
||||||
|
|
||||||
|
// int
|
||||||
|
|
||||||
|
impl From<i64> for Object {
|
||||||
|
fn from(from: i64) -> Self {
|
||||||
|
Object::Int(from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bytes
|
||||||
|
|
||||||
|
impl<'a> From<&'a [u8]> for Bytes {
|
||||||
|
fn from(from: &'a [u8]) -> Self {
|
||||||
|
Bytes(from.to_vec())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a str> for Bytes {
|
||||||
|
fn from(from: &'a str) -> Self {
|
||||||
|
Bytes(from.as_bytes().to_vec())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<u8>> for Bytes {
|
||||||
|
fn from(from: Vec<u8>) -> Self {
|
||||||
|
Bytes(from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a [u8]> for Object {
|
||||||
|
fn from(from: &'a [u8]) -> Self {
|
||||||
|
Object::Bytes(Bytes(from.to_vec()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a str> for Object {
|
||||||
|
fn from(from: &'a str) -> Self {
|
||||||
|
Object::Bytes(Bytes(from.as_bytes().to_vec()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<u8>> for Object {
|
||||||
|
fn from(from: Vec<u8>) -> Self {
|
||||||
|
Object::Bytes(Bytes(from))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Bytes> for Object {
|
||||||
|
fn from(from: Bytes) -> Self {
|
||||||
|
Object::Bytes(from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// list
|
||||||
|
|
||||||
|
impl<T> From<Vec<T>> for List where T: Into<Object> {
|
||||||
|
fn from(from: Vec<T>) -> Self {
|
||||||
|
List(from.into_iter().map(Into::into).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<Vec<T>> for Object where T: Into<Object> {
|
||||||
|
fn from(from: Vec<T>) -> Self {
|
||||||
|
Object::List(List(from.into_iter().map(Into::into).collect()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<List> for Object {
|
||||||
|
fn from(from: List) -> Self {
|
||||||
|
Object::List(from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// dict
|
||||||
|
|
||||||
|
impl From<BTreeMap<Bytes, Object>> for Dict {
|
||||||
|
fn from(from: BTreeMap<Bytes, Object>) -> Self {
|
||||||
|
Dict(from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BTreeMap<Bytes, Object>> for Object {
|
||||||
|
fn from(from: BTreeMap<Bytes, Object>) -> Self {
|
||||||
|
Object::Dict(Dict(from))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Dict> for Object {
|
||||||
|
fn from(from: Dict) -> Self {
|
||||||
|
Object::Dict(from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ pub struct Metainfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Metainfo {
|
impl Metainfo {
|
||||||
pub fn from_bencode(obj: Object) -> Option<Metainfo> {
|
pub fn from_bencode<O>(obj: O) -> Option<Metainfo> where O: Into<Object> {
|
||||||
|
let obj = obj.into();
|
||||||
let metainfo = ts!(obj.as_dict());
|
let metainfo = ts!(obj.as_dict());
|
||||||
let info = ts!(metainfo.get_dict("info"));
|
let info = ts!(metainfo.get_dict("info"));
|
||||||
|
|
||||||
@@ -106,23 +107,23 @@ fn piece_from_slice(src: &[u8]) -> [u8; 20] {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use bencode::{Bytes, Dict, List, Object};
|
use bencode::{Dict, List, Object};
|
||||||
use metainfo::Metainfo;
|
use metainfo::Metainfo;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_single_file() {
|
fn test_single_file() {
|
||||||
let mut meta = Dict::new();
|
let mut meta = Dict::new();
|
||||||
meta.insert(Bytes::new("announce"), Bytes::wrap("http://ubuntu.com/tracker:6969"));
|
meta.insert("announce", "http://ubuntu.com/tracker:6969");
|
||||||
|
|
||||||
let mut info = Dict::new();
|
let mut info = Dict::new();
|
||||||
info.insert(Bytes::new("name"), Bytes::wrap("ubuntu-16.04-desktop.iso"));
|
info.insert("name", "ubuntu-16.04-desktop.iso");
|
||||||
info.insert(Bytes::new("length"), Object::Int(1024 * 1024 * 1024));
|
info.insert("length", 1024 * 1024 * 1024);
|
||||||
info.insert(Bytes::new("piece length"), Object::Int(1024 * 512));
|
info.insert("piece length", 1024 * 512);
|
||||||
info.insert(Bytes::new("pieces"), Bytes::wrap(""));
|
info.insert("pieces", "");
|
||||||
|
|
||||||
meta.insert(Bytes::new("info"), Object::Dict(info));
|
meta.insert("info", info);
|
||||||
|
|
||||||
let metainfo = Metainfo::from_bencode(Object::Dict(meta)).unwrap();
|
let metainfo = Metainfo::from_bencode(meta).unwrap();
|
||||||
assert_eq!(metainfo.announce, "http://ubuntu.com/tracker:6969");
|
assert_eq!(metainfo.announce, "http://ubuntu.com/tracker:6969");
|
||||||
assert_eq!(metainfo.piece_length, 1024 * 512);
|
assert_eq!(metainfo.piece_length, 1024 * 512);
|
||||||
|
|
||||||
@@ -134,27 +135,27 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_multiple_files() {
|
fn test_multiple_files() {
|
||||||
let mut meta = Dict::new();
|
let mut meta = Dict::new();
|
||||||
meta.insert(Bytes::new("announce"), Bytes::wrap("http://ubuntu.com/tracker:6969"));
|
meta.insert("announce", "http://ubuntu.com/tracker:6969");
|
||||||
|
|
||||||
let mut info = Dict::new();
|
let mut info = Dict::new();
|
||||||
info.insert(Bytes::new("name"), Bytes::wrap("base_folder"));
|
info.insert("name", "base_folder");
|
||||||
info.insert(Bytes::new("piece length"), Object::Int(1024 * 512));
|
info.insert("piece length", 1024 * 512);
|
||||||
info.insert(Bytes::new("pieces"), Bytes::wrap(""));
|
info.insert("pieces", "");
|
||||||
|
|
||||||
let mut files = List::new();
|
let mut files = List::new();
|
||||||
let mut file1 = Dict::new();
|
let mut file1 = Dict::new();
|
||||||
file1.insert(Bytes::new("path"), List::wrap(vec![Bytes::wrap("folder2"), Bytes::wrap("image.txt")]));
|
file1.insert("path", vec!["folder2", "image.txt"]);
|
||||||
file1.insert(Bytes::new("length"), Object::Int(512));
|
file1.insert("length", 512);
|
||||||
files.push(Object::Dict(file1));
|
files.push(file1);
|
||||||
let mut file2 = Dict::new();
|
let mut file2 = Dict::new();
|
||||||
file2.insert(Bytes::new("path"), List::wrap(vec![Bytes::wrap("txt.jpg")]));
|
file2.insert("path", vec!["txt.jpg"]);
|
||||||
file2.insert(Bytes::new("length"), Object::Int(64));
|
file2.insert("length", 64);
|
||||||
files.push(Object::Dict(file2));
|
files.push(file2);
|
||||||
info.insert(Bytes::new("files"), Object::List(files));
|
info.insert("files", Object::List(files));
|
||||||
|
|
||||||
meta.insert(Bytes::new("info"), Object::Dict(info));
|
meta.insert("info", info);
|
||||||
|
|
||||||
let metainfo = Metainfo::from_bencode(Object::Dict(meta)).unwrap();
|
let metainfo = Metainfo::from_bencode(meta).unwrap();
|
||||||
assert_eq!(metainfo.announce, "http://ubuntu.com/tracker:6969");
|
assert_eq!(metainfo.announce, "http://ubuntu.com/tracker:6969");
|
||||||
assert_eq!(metainfo.piece_length, 1024 * 512);
|
assert_eq!(metainfo.piece_length, 1024 * 512);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user