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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use token::*;
use parse::*;


/// Token iterator instance
///
/// # Example
/// ```ignore
/// extern crate lazy_bencoding;
/// use lazy_bencoding::*;
///
/// let bencoded = BEncoded::new(&data[..]);
/// let name = bencoded.get(b"info").get(b"name").get_utf8_string();
/// ```
///
/// You may now:
///
/// * iterate over `bencoded` to process all [`Token`](enum.Token.html) in order, or
/// * traverse the structure using [`get()`](#method.get) and [`get_utf8_string()`](#method.get_utf8_string).
#[derive(Clone)]
pub struct BEncoded<'a> {
    /// Next data to parse, only a cheap reference and updated with
    /// each [`next()`](#method.next)
    pub data: &'a [u8],
    /// Keeping track of the depth, for:
    ///
    /// * traversing, and
    /// * stopping after one item, before trailing data.
    pub depth: i16,
}

impl<'a> BEncoded<'a> {
    /// Construct from a pointer to data
    pub fn new(data: &'a [u8]) -> Self {
        BEncoded {
            data: data,
            depth: 0,
        }
    }

    /// Construct with empty data,
    /// used for when traversing fails
    pub fn empty() -> BEncoded<'static> {
        BEncoded {
            data: b"",
            depth: 0
        }
    }
}

/// The [token](enum.Token.html) stream. Advances `self.data` and updates `self.depth`.
impl<'a> Iterator for BEncoded<'a> {
    type Item = Token<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.depth < 0 {
            // Already done
            return None;
        }

        match parse_token(self.data) {
            None => None,
            Some((token, rest)) => {
                match token {
                    Token::ListStart | Token::DictStart =>
                        self.depth += 1,
                    Token::End =>
                        self.depth -= 1,
                    _ =>
                        ()
                }
                if self.depth == 0 {
                    // Emit only one item at level 0
                    self.depth -= 1;
                }

                self.data = rest;
                Some(token)
            }
        }
    }
}

#[cfg(test)]
mod tests {
    fn expect<'a>(input: &'a [u8], expected: &[::Token<'a>]) {
        let bencoded = ::BEncoded::new(input);
        let result = bencoded.collect::<Vec<::Token<'a>>>();
        assert_eq!(result, expected);
    }

    #[test]
    fn test_strings() {
        expect(b"4:spam", &[::Token::ByteString(b"spam")]);
    }

    #[test]
    fn test_integers() {
        expect(b"i3e", &[::Token::Integer(b"3")]);
        expect(b"i-3e", &[::Token::Integer(b"-3")]);
        expect(b"i0e", &[::Token::Integer(b"0")]);
    }

    #[test]
    fn test_lists() {
        expect(b"l4:spam4:eggse",
               &[::Token::ListStart,
                 ::Token::ByteString(b"spam"),
                 ::Token::ByteString(b"eggs"),
                 ::Token::End]);
    }

    #[test]
    fn test_dict() {
        expect(b"d3:cow3:moo4:spam4:eggse",
               &[::Token::DictStart,
                 ::Token::ByteString(b"cow"),
                 ::Token::ByteString(b"moo"),
                 ::Token::ByteString(b"spam"),
                 ::Token::ByteString(b"eggs"),
                 ::Token::End]);
        expect(b"d4:spaml1:a1:bee",
               &[::Token::DictStart,
                 ::Token::ByteString(b"spam"),
                 ::Token::ListStart,
                 ::Token::ByteString(b"a"),
                 ::Token::ByteString(b"b"),
                 ::Token::End,
                 ::Token::End]);
    }

    #[test]
    fn test_only_one() {
        expect(b"4:spam4:eggs", &[::Token::ByteString(b"spam")]);
    }
}