use std::collections::BTreeMap;
use std::str;
use crate::access::bencode::{BRefAccess, BRefAccessExt, RefKind};
use crate::access::dict::BDictAccess;
use crate::access::list::BListAccess;
use crate::error::{BencodeParseError, BencodeParseErrorKind, BencodeParseResult};
use crate::reference::decode;
use crate::reference::decode_opt::BDecodeOpt;
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
pub enum Inner<'a> {
Int(i64, &'a [u8]),
Bytes(&'a [u8], &'a [u8]),
List(Vec<BencodeRef<'a>>, &'a [u8]),
Dict(BTreeMap<&'a [u8], BencodeRef<'a>>, &'a [u8]),
}
impl<'a> From<Inner<'a>> for BencodeRef<'a> {
fn from(val: Inner<'a>) -> Self {
BencodeRef { inner: val }
}
}
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
pub struct BencodeRef<'a> {
inner: Inner<'a>,
}
impl<'a> BencodeRef<'a> {
#[allow(clippy::missing_errors_doc)]
pub fn decode(bytes: &'a [u8], opts: BDecodeOpt) -> BencodeParseResult<BencodeRef<'a>> {
let (bencode, end_pos) = decode::decode(bytes, 0, opts, 0)?;
if end_pos != bytes.len() && opts.enforce_full_decode() {
return Err(BencodeParseError::from_kind(BencodeParseErrorKind::BytesEmpty {
pos: end_pos,
}));
}
Ok(bencode)
}
#[must_use]
pub fn buffer(&self) -> &'a [u8] {
#[allow(clippy::match_same_arms)]
match self.inner {
Inner::Int(_, buffer) => buffer,
Inner::Bytes(_, buffer) => buffer,
Inner::List(_, buffer) => buffer,
Inner::Dict(_, buffer) => buffer,
}
}
}
impl<'a> BRefAccess for BencodeRef<'a> {
type BKey = &'a [u8];
type BType = BencodeRef<'a>;
fn kind<'b>(&'b self) -> RefKind<'b, &'a [u8], BencodeRef<'a>> {
match self.inner {
Inner::Int(n, _) => RefKind::Int(n),
Inner::Bytes(n, _) => RefKind::Bytes(n),
Inner::List(ref n, _) => RefKind::List(n),
Inner::Dict(ref n, _) => RefKind::Dict(n),
}
}
fn str(&self) -> Option<&str> {
self.str_ext()
}
fn int(&self) -> Option<i64> {
match self.inner {
Inner::Int(n, _) => Some(n),
_ => None,
}
}
fn bytes(&self) -> Option<&[u8]> {
self.bytes_ext()
}
fn list(&self) -> Option<&dyn BListAccess<BencodeRef<'a>>> {
match self.inner {
Inner::List(ref n, _) => Some(n),
_ => None,
}
}
fn dict(&self) -> Option<&dyn BDictAccess<&'a [u8], BencodeRef<'a>>> {
match self.inner {
Inner::Dict(ref n, _) => Some(n),
_ => None,
}
}
}
impl<'a> BRefAccessExt<'a> for BencodeRef<'a> {
fn str_ext(&self) -> Option<&'a str> {
let bytes = self.bytes_ext()?;
match str::from_utf8(bytes) {
Ok(n) => Some(n),
Err(_) => None,
}
}
fn bytes_ext(&self) -> Option<&'a [u8]> {
match self.inner {
Inner::Bytes(n, _) => Some(&n[0..]),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use std::default::Default;
use crate::access::bencode::BRefAccess;
use crate::reference::bencode_ref::BencodeRef;
use crate::reference::decode_opt::BDecodeOpt;
#[test]
fn positive_int_buffer() {
let int_bytes = b"i-500e"; let bencode = BencodeRef::decode(&int_bytes[..], BDecodeOpt::default()).unwrap();
assert_eq!(int_bytes, bencode.buffer());
}
#[test]
fn positive_bytes_buffer() {
let bytes_bytes = b"3:asd"; let bencode = BencodeRef::decode(&bytes_bytes[..], BDecodeOpt::default()).unwrap();
assert_eq!(bytes_bytes, bencode.buffer());
}
#[test]
fn positive_list_buffer() {
let list_bytes = b"l3:asde"; let bencode = BencodeRef::decode(&list_bytes[..], BDecodeOpt::default()).unwrap();
assert_eq!(list_bytes, bencode.buffer());
}
#[test]
fn positive_dict_buffer() {
let dict_bytes = b"d3:asd3:asde"; let bencode = BencodeRef::decode(&dict_bytes[..], BDecodeOpt::default()).unwrap();
assert_eq!(dict_bytes, bencode.buffer());
}
#[test]
fn positive_list_nested_int_buffer() {
let nested_int_bytes = b"li-500ee"; let bencode = BencodeRef::decode(&nested_int_bytes[..], BDecodeOpt::default()).unwrap();
let bencode_list = bencode.list().unwrap();
let bencode_int = bencode_list.get(0).unwrap();
let int_bytes = b"i-500e"; assert_eq!(int_bytes, bencode_int.buffer());
}
#[test]
fn positive_dict_nested_int_buffer() {
let nested_int_bytes = b"d3:asdi-500ee"; let bencode = BencodeRef::decode(&nested_int_bytes[..], BDecodeOpt::default()).unwrap();
let bencode_dict = bencode.dict().unwrap();
let bencode_int = bencode_dict.lookup(&b"asd"[..]).unwrap();
let int_bytes = b"i-500e"; assert_eq!(int_bytes, bencode_int.buffer());
}
#[test]
fn positive_list_nested_bytes_buffer() {
let nested_bytes_bytes = b"l3:asde"; let bencode = BencodeRef::decode(&nested_bytes_bytes[..], BDecodeOpt::default()).unwrap();
let bencode_list = bencode.list().unwrap();
let bencode_bytes = bencode_list.get(0).unwrap();
let bytes_bytes = b"3:asd"; assert_eq!(bytes_bytes, bencode_bytes.buffer());
}
#[test]
fn positive_dict_nested_bytes_buffer() {
let nested_bytes_bytes = b"d3:asd3:asde"; let bencode = BencodeRef::decode(&nested_bytes_bytes[..], BDecodeOpt::default()).unwrap();
let bencode_dict = bencode.dict().unwrap();
let bencode_bytes = bencode_dict.lookup(&b"asd"[..]).unwrap();
let bytes_bytes = b"3:asd"; assert_eq!(bytes_bytes, bencode_bytes.buffer());
}
#[test]
fn positive_list_nested_list_buffer() {
let nested_list_bytes = b"ll3:asdee"; let bencode = BencodeRef::decode(&nested_list_bytes[..], BDecodeOpt::default()).unwrap();
let bencode_list = bencode.list().unwrap();
let bencode_list = bencode_list.get(0).unwrap();
let list_bytes = b"l3:asde"; assert_eq!(list_bytes, bencode_list.buffer());
}
#[test]
fn positive_dict_nested_list_buffer() {
let nested_list_bytes = b"d3:asdl3:asdee"; let bencode = BencodeRef::decode(&nested_list_bytes[..], BDecodeOpt::default()).unwrap();
let bencode_dict = bencode.dict().unwrap();
let bencode_list = bencode_dict.lookup(&b"asd"[..]).unwrap();
let list_bytes = b"l3:asde"; assert_eq!(list_bytes, bencode_list.buffer());
}
#[test]
fn positive_list_nested_dict_buffer() {
let nested_dict_bytes = b"ld3:asd3:asdee"; let bencode = BencodeRef::decode(&nested_dict_bytes[..], BDecodeOpt::default()).unwrap();
let bencode_list = bencode.list().unwrap();
let bencode_dict = bencode_list.get(0).unwrap();
let dict_bytes = b"d3:asd3:asde"; assert_eq!(dict_bytes, bencode_dict.buffer());
}
#[test]
fn positive_dict_nested_dict_buffer() {
let nested_dict_bytes = b"d3:asdd3:asd3:asdee"; let bencode = BencodeRef::decode(&nested_dict_bytes[..], BDecodeOpt::default()).unwrap();
let bencode_dict = bencode.dict().unwrap();
let bencode_dict = bencode_dict.lookup(&b"asd"[..]).unwrap();
let dict_bytes = b"d3:asd3:asde"; assert_eq!(dict_bytes, bencode_dict.buffer());
}
}