torrust_tracker_contrib_bencode/reference/
bencode_ref.rs

1use std::collections::BTreeMap;
2use std::str;
3
4use crate::access::bencode::{BRefAccess, BRefAccessExt, RefKind};
5use crate::access::dict::BDictAccess;
6use crate::access::list::BListAccess;
7use crate::error::{BencodeParseError, BencodeParseResult};
8use crate::reference::decode;
9use crate::reference::decode_opt::BDecodeOpt;
10
11/// Bencode object that holds references to the underlying data.
12#[derive(Debug, Eq, PartialEq, Clone, Hash)]
13pub enum Inner<'a> {
14    /// Bencode Integer.
15    Int(i64, &'a [u8]),
16    /// Bencode Bytes.
17    Bytes(&'a [u8], &'a [u8]),
18    /// Bencode List.
19    List(Vec<BencodeRef<'a>>, &'a [u8]),
20    /// Bencode Dictionary.
21    Dict(BTreeMap<&'a [u8], BencodeRef<'a>>, &'a [u8]),
22}
23
24impl<'a> From<Inner<'a>> for BencodeRef<'a> {
25    fn from(val: Inner<'a>) -> Self {
26        BencodeRef { inner: val }
27    }
28}
29
30/// `BencodeRef` object that stores references to some buffer.
31#[derive(Debug, Eq, PartialEq, Clone, Hash)]
32pub struct BencodeRef<'a> {
33    inner: Inner<'a>,
34}
35
36impl<'a> BencodeRef<'a> {
37    /// Decode the given bytes into a `BencodeRef` using the given decode options.
38    #[allow(clippy::missing_errors_doc)]
39    pub fn decode(bytes: &'a [u8], opts: BDecodeOpt) -> BencodeParseResult<BencodeRef<'a>> {
40        // Apply try so any errors return before the eof check
41        let (bencode, end_pos) = decode::decode(bytes, 0, opts, 0)?;
42
43        if end_pos != bytes.len() && opts.enforce_full_decode() {
44            return Err(BencodeParseError::BytesEmpty { pos: end_pos });
45        }
46
47        Ok(bencode)
48    }
49
50    /// Get a byte slice of the current bencode byte representation.
51    #[must_use]
52    pub fn buffer(&self) -> &'a [u8] {
53        #[allow(clippy::match_same_arms)]
54        match self.inner {
55            Inner::Int(_, buffer) => buffer,
56            Inner::Bytes(_, buffer) => buffer,
57            Inner::List(_, buffer) => buffer,
58            Inner::Dict(_, buffer) => buffer,
59        }
60    }
61}
62
63impl<'a> BRefAccess for BencodeRef<'a> {
64    type BKey = &'a [u8];
65    type BType = BencodeRef<'a>;
66
67    fn kind<'b>(&'b self) -> RefKind<'b, &'a [u8], BencodeRef<'a>> {
68        match self.inner {
69            Inner::Int(n, _) => RefKind::Int(n),
70            Inner::Bytes(n, _) => RefKind::Bytes(n),
71            Inner::List(ref n, _) => RefKind::List(n),
72            Inner::Dict(ref n, _) => RefKind::Dict(n),
73        }
74    }
75
76    fn str(&self) -> Option<&str> {
77        self.str_ext()
78    }
79
80    fn int(&self) -> Option<i64> {
81        match self.inner {
82            Inner::Int(n, _) => Some(n),
83            _ => None,
84        }
85    }
86
87    fn bytes(&self) -> Option<&[u8]> {
88        self.bytes_ext()
89    }
90
91    fn list(&self) -> Option<&dyn BListAccess<BencodeRef<'a>>> {
92        match self.inner {
93            Inner::List(ref n, _) => Some(n),
94            _ => None,
95        }
96    }
97
98    fn dict(&self) -> Option<&dyn BDictAccess<&'a [u8], BencodeRef<'a>>> {
99        match self.inner {
100            Inner::Dict(ref n, _) => Some(n),
101            _ => None,
102        }
103    }
104}
105
106impl<'a> BRefAccessExt<'a> for BencodeRef<'a> {
107    fn str_ext(&self) -> Option<&'a str> {
108        let bytes = self.bytes_ext()?;
109
110        match str::from_utf8(bytes) {
111            Ok(n) => Some(n),
112            Err(_) => None,
113        }
114    }
115
116    fn bytes_ext(&self) -> Option<&'a [u8]> {
117        match self.inner {
118            Inner::Bytes(n, _) => Some(&n[0..]),
119            _ => None,
120        }
121    }
122}
123
124#[cfg(test)]
125mod tests {
126
127    use crate::access::bencode::BRefAccess;
128    use crate::reference::bencode_ref::BencodeRef;
129    use crate::reference::decode_opt::BDecodeOpt;
130
131    #[test]
132    fn positive_int_buffer() {
133        let int_bytes = b"i-500e"; // cspell:disable-line
134        let bencode = BencodeRef::decode(&int_bytes[..], BDecodeOpt::default()).unwrap();
135
136        assert_eq!(int_bytes, bencode.buffer());
137    }
138
139    #[test]
140    fn positive_bytes_buffer() {
141        let bytes_bytes = b"3:asd"; // cspell:disable-line
142        let bencode = BencodeRef::decode(&bytes_bytes[..], BDecodeOpt::default()).unwrap();
143
144        assert_eq!(bytes_bytes, bencode.buffer());
145    }
146
147    #[test]
148    fn positive_list_buffer() {
149        let list_bytes = b"l3:asde"; // cspell:disable-line
150        let bencode = BencodeRef::decode(&list_bytes[..], BDecodeOpt::default()).unwrap();
151
152        assert_eq!(list_bytes, bencode.buffer());
153    }
154
155    #[test]
156    fn positive_dict_buffer() {
157        let dict_bytes = b"d3:asd3:asde"; // cspell:disable-line
158        let bencode = BencodeRef::decode(&dict_bytes[..], BDecodeOpt::default()).unwrap();
159
160        assert_eq!(dict_bytes, bencode.buffer());
161    }
162
163    #[test]
164    fn positive_list_nested_int_buffer() {
165        let nested_int_bytes = b"li-500ee"; // cspell:disable-line
166        let bencode = BencodeRef::decode(&nested_int_bytes[..], BDecodeOpt::default()).unwrap();
167
168        let bencode_list = bencode.list().unwrap();
169        let bencode_int = bencode_list.get(0).unwrap();
170
171        let int_bytes = b"i-500e"; // cspell:disable-line
172        assert_eq!(int_bytes, bencode_int.buffer());
173    }
174
175    #[test]
176    fn positive_dict_nested_int_buffer() {
177        let nested_int_bytes = b"d3:asdi-500ee"; // cspell:disable-line
178        let bencode = BencodeRef::decode(&nested_int_bytes[..], BDecodeOpt::default()).unwrap();
179
180        let bencode_dict = bencode.dict().unwrap();
181        /* cspell:disable-next-line */
182        let bencode_int = bencode_dict.lookup(&b"asd"[..]).unwrap();
183
184        let int_bytes = b"i-500e"; // cspell:disable-line
185        assert_eq!(int_bytes, bencode_int.buffer());
186    }
187
188    #[test]
189    fn positive_list_nested_bytes_buffer() {
190        let nested_bytes_bytes = b"l3:asde"; // cspell:disable-line
191        let bencode = BencodeRef::decode(&nested_bytes_bytes[..], BDecodeOpt::default()).unwrap();
192
193        let bencode_list = bencode.list().unwrap();
194        let bencode_bytes = bencode_list.get(0).unwrap();
195
196        let bytes_bytes = b"3:asd"; // cspell:disable-line
197        assert_eq!(bytes_bytes, bencode_bytes.buffer());
198    }
199
200    #[test]
201    fn positive_dict_nested_bytes_buffer() {
202        let nested_bytes_bytes = b"d3:asd3:asde"; // cspell:disable-line
203        let bencode = BencodeRef::decode(&nested_bytes_bytes[..], BDecodeOpt::default()).unwrap();
204
205        let bencode_dict = bencode.dict().unwrap();
206        /* cspell:disable-next-line */
207        let bencode_bytes = bencode_dict.lookup(&b"asd"[..]).unwrap();
208
209        let bytes_bytes = b"3:asd"; // cspell:disable-line
210        assert_eq!(bytes_bytes, bencode_bytes.buffer());
211    }
212
213    #[test]
214    fn positive_list_nested_list_buffer() {
215        let nested_list_bytes = b"ll3:asdee"; // cspell:disable-line
216        let bencode = BencodeRef::decode(&nested_list_bytes[..], BDecodeOpt::default()).unwrap();
217
218        let bencode_list = bencode.list().unwrap();
219        let bencode_list = bencode_list.get(0).unwrap();
220
221        let list_bytes = b"l3:asde"; // cspell:disable-line
222        assert_eq!(list_bytes, bencode_list.buffer());
223    }
224
225    #[test]
226    fn positive_dict_nested_list_buffer() {
227        let nested_list_bytes = b"d3:asdl3:asdee"; // cspell:disable-line
228        let bencode = BencodeRef::decode(&nested_list_bytes[..], BDecodeOpt::default()).unwrap();
229
230        let bencode_dict = bencode.dict().unwrap();
231        /* cspell:disable-next-line */
232        let bencode_list = bencode_dict.lookup(&b"asd"[..]).unwrap();
233
234        let list_bytes = b"l3:asde"; // cspell:disable-line
235        assert_eq!(list_bytes, bencode_list.buffer());
236    }
237
238    #[test]
239    fn positive_list_nested_dict_buffer() {
240        let nested_dict_bytes = b"ld3:asd3:asdee"; // cspell:disable-line
241        let bencode = BencodeRef::decode(&nested_dict_bytes[..], BDecodeOpt::default()).unwrap();
242
243        let bencode_list = bencode.list().unwrap();
244        let bencode_dict = bencode_list.get(0).unwrap();
245
246        let dict_bytes = b"d3:asd3:asde"; // cspell:disable-line
247        assert_eq!(dict_bytes, bencode_dict.buffer());
248    }
249
250    #[test]
251    fn positive_dict_nested_dict_buffer() {
252        let nested_dict_bytes = b"d3:asdd3:asd3:asdee"; // cspell:disable-line
253        let bencode = BencodeRef::decode(&nested_dict_bytes[..], BDecodeOpt::default()).unwrap();
254
255        let bencode_dict = bencode.dict().unwrap();
256        /* cspell:disable-next-line */
257        let bencode_dict = bencode_dict.lookup(&b"asd"[..]).unwrap();
258
259        let dict_bytes = b"d3:asd3:asde"; // cspell:disable-line
260        assert_eq!(dict_bytes, bencode_dict.buffer());
261    }
262}