1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
use std::collections::HashMap;

use Bencode::*;

use crate::error::{
    BencodeParserError,
    BencodeParserError::*,
    invalid_variant_method,
};
use crate::parser::parse_child;

/// A nested enum containing the parsed Bencode file structure.
pub enum Bencode {
    /// A value. (or a 'leaf' in tree terminology) which can be converted into a value with any
    /// of: `as_string()`, `as_bytes()`, or `as_usize()`.
    ///
    /// Possible options:
    /// - An Integer. eg: `i123e`. indicated by `i` and terminated by `e`.
    /// - A ByteString. eg: `3:cat`. stars with an integer length, followed by `:` as a separator,
    ///   then the bytes.
    Value(Vec<u8>),
    /// A list of Bencode structs which can be destructured into a Vec with `as_vec()`.
    List(Vec<Bencode>),
    /// A map of bencoded values. All keys are Vec<u8>, and values are nested Bencode structs.
    /// This can be destructured with `as_map()`.
    Dict(HashMap<Vec<u8>, Bencode>),
}

/// Attempts to parse the given bytes into a Bencode object.
pub fn from_bytes(bytes: &[u8]) -> Result<Bencode, BencodeParserError> {
    match parse_child(bytes) {
        Ok((bencode, _)) => Ok(bencode),
        Err(e) => Err(e)
    }
}

impl Bencode {
    /// Attempt to parse a `Bencode::Value` variant into a `usize`.
    ///
    /// Can return:
    /// - `InvalidVariantMethod`, if called on any of `Bencode::{List, Dict}`.
    /// - `InvalidUTF8String`, if the bytes cannot be parsed as a UTF8, ASCII string.
    /// - `InvalidASCIIInteger`, if the ASCII bytes aren't a valid base 10 number.
    pub fn as_usize(&self) -> Result<usize, BencodeParserError> {
        match self {
            Value(_) => match self.as_string() {
                Ok(str) => match usize::from_str_radix(str.as_str(), 10) {
                    Ok(val) => Ok(val),
                    Err(err) => Err(InvalidASCIIInteger(err))
                },
                Err(e) => Err(e),
            }
            _ => Err(invalid_variant_method("as_usize()", self))
        }
    }
    /// Attempt to parse a `Bencode::Value` variant into a `String`.
    ///
    /// Can return:
    /// - `InvalidVariantMethod`, if called on any of `Bencode::{List, Dict}`.
    /// - `InvalidUTF8String`, if the bytes cannot be parsed as a UTF8, ASCII string.
    pub fn as_string(&self) -> Result<String, BencodeParserError> {
        match self {
            Value(val) => {
                match String::from_utf8(val.to_owned()) {
                    Ok(val) => Ok(val),
                    Err(err) => Err(InvalidUTF8String(err)),
                }
            }
            _ => Err(invalid_variant_method("as_string()", self))
        }
    }
    /// Destructures a `Bencode::Value` into a `Vec<u8>`. This method will always succeed if applied
    /// to a `Bencode::Value`.
    ///
    /// Can return:
    /// - `InvalidVariantMethod`, if called on any of `Bencode::{List, Dict}`.
    pub fn as_bytes(&self) -> Result<&Vec<u8>, BencodeParserError> {
        match self {
            Value(val) => Ok(val),
            _ => Err(invalid_variant_method("as_bytes()", self))
        }
    }
    /// Attempt to parse a `Bencode::List` into a `Vec<Bencode>`. This method will always succeed
    /// if applied to a `Bencode::List`.
    ///
    /// Can return:
    /// - `InvalidVariantMethod`, if called on any of `Bencode::{Value, Dict}`.
    pub fn as_vec(&self) -> Result<&Vec<Bencode>, BencodeParserError> {
        match self {
            List(list) => Ok(list),
            _ => Err(invalid_variant_method("as_list()", self))
        }
    }
    /// Attempt to parse a `Bencode::Dict` into a `HashMap<Vec<u8>, Bencode>`. This method will
    /// always succeed if applied to a `Bencode::Dict`.
    ///
    /// Can return:
    /// - `InvalidVariantMethod`, if called on any of `Bencode::{Value, List}`.
    pub fn as_map(&self) -> Result<&HashMap<Vec<u8>, Bencode>, BencodeParserError> {
        match self {
            Dict(dict) => Ok(dict),
            _ => Err(invalid_variant_method("as_dict()", self))
        }
    }
}