sveppa_bencode/bencode.rs
1use std::collections::HashMap;
2
3use Bencode::*;
4
5use crate::error::{
6 BencodeParserError,
7 BencodeParserError::*,
8 invalid_variant_method,
9};
10use crate::parser::parse_child;
11
12/// A nested enum containing the parsed Bencode file structure.
13pub enum Bencode {
14 /// A value. (or a 'leaf' in tree terminology) which can be converted into a value with any
15 /// of: `as_string()`, `as_bytes()`, or `as_usize()`.
16 ///
17 /// Possible options:
18 /// - An Integer. eg: `i123e`. indicated by `i` and terminated by `e`.
19 /// - A ByteString. eg: `3:cat`. stars with an integer length, followed by `:` as a separator,
20 /// then the bytes.
21 Value(Vec<u8>),
22 /// A list of Bencode structs which can be destructured into a Vec with `as_vec()`.
23 List(Vec<Bencode>),
24 /// A map of bencoded values. All keys are Vec<u8>, and values are nested Bencode structs.
25 /// This can be destructured with `as_map()`.
26 Dict(HashMap<Vec<u8>, Bencode>),
27}
28
29/// Attempts to parse the given bytes into a Bencode object.
30pub fn from_bytes(bytes: &[u8]) -> Result<Bencode, BencodeParserError> {
31 match parse_child(bytes) {
32 Ok((bencode, _)) => Ok(bencode),
33 Err(e) => Err(e)
34 }
35}
36
37impl Bencode {
38 /// Attempt to parse a `Bencode::Value` variant into a `usize`.
39 ///
40 /// Can return:
41 /// - `InvalidVariantMethod`, if called on any of `Bencode::{List, Dict}`.
42 /// - `InvalidUTF8String`, if the bytes cannot be parsed as a UTF8, ASCII string.
43 /// - `InvalidASCIIInteger`, if the ASCII bytes aren't a valid base 10 number.
44 pub fn as_usize(&self) -> Result<usize, BencodeParserError> {
45 match self {
46 Value(_) => match self.as_string() {
47 Ok(str) => match usize::from_str_radix(str.as_str(), 10) {
48 Ok(val) => Ok(val),
49 Err(err) => Err(InvalidASCIIInteger(err))
50 },
51 Err(e) => Err(e),
52 }
53 _ => Err(invalid_variant_method("as_usize()", self))
54 }
55 }
56 /// Attempt to parse a `Bencode::Value` variant into a `String`.
57 ///
58 /// Can return:
59 /// - `InvalidVariantMethod`, if called on any of `Bencode::{List, Dict}`.
60 /// - `InvalidUTF8String`, if the bytes cannot be parsed as a UTF8, ASCII string.
61 pub fn as_string(&self) -> Result<String, BencodeParserError> {
62 match self {
63 Value(val) => {
64 match String::from_utf8(val.to_owned()) {
65 Ok(val) => Ok(val),
66 Err(err) => Err(InvalidUTF8String(err)),
67 }
68 }
69 _ => Err(invalid_variant_method("as_string()", self))
70 }
71 }
72 /// Destructures a `Bencode::Value` into a `Vec<u8>`. This method will always succeed if applied
73 /// to a `Bencode::Value`.
74 ///
75 /// Can return:
76 /// - `InvalidVariantMethod`, if called on any of `Bencode::{List, Dict}`.
77 pub fn as_bytes(&self) -> Result<&Vec<u8>, BencodeParserError> {
78 match self {
79 Value(val) => Ok(val),
80 _ => Err(invalid_variant_method("as_bytes()", self))
81 }
82 }
83 /// Attempt to parse a `Bencode::List` into a `Vec<Bencode>`. This method will always succeed
84 /// if applied to a `Bencode::List`.
85 ///
86 /// Can return:
87 /// - `InvalidVariantMethod`, if called on any of `Bencode::{Value, Dict}`.
88 pub fn as_vec(&self) -> Result<&Vec<Bencode>, BencodeParserError> {
89 match self {
90 List(list) => Ok(list),
91 _ => Err(invalid_variant_method("as_list()", self))
92 }
93 }
94 /// Attempt to parse a `Bencode::Dict` into a `HashMap<Vec<u8>, Bencode>`. This method will
95 /// always succeed if applied to a `Bencode::Dict`.
96 ///
97 /// Can return:
98 /// - `InvalidVariantMethod`, if called on any of `Bencode::{Value, List}`.
99 pub fn as_map(&self) -> Result<&HashMap<Vec<u8>, Bencode>, BencodeParserError> {
100 match self {
101 Dict(dict) => Ok(dict),
102 _ => Err(invalid_variant_method("as_dict()", self))
103 }
104 }
105}