1use core::str;
3use std::{
4 fmt::{self},
5 io,
6};
7
8use thiserror::Error;
9
10use crate::rw;
11
12use super::BencodeType;
13
14#[derive(Debug, Error)]
16pub enum Error {
17 #[error("I/O error: {0}")]
19 Io(#[from] io::Error),
20
21 #[error("R/W error: {0}")]
23 Rw(#[from] rw::error::Error),
24
25 #[error("Read byte after peeking does match peeked byte; {0}; {1}")]
31 ReadByteAfterPeekingDoesMatchPeekedByte(ReadContext, WriteContext),
32
33 #[error("Unrecognized first byte for new bencoded value; {0}; {1}")]
39 UnrecognizedFirstBencodeValueByte(ReadContext, WriteContext),
40
41 #[error("Unexpected byte parsing integer; {0}; {1}")]
48 UnexpectedByteParsingInteger(ReadContext, WriteContext),
49
50 #[error("Unexpected end of input parsing integer; {0}; {1}")]
54 UnexpectedEndOfInputParsingInteger(ReadContext, WriteContext),
55
56 #[error("Leading zeros in integers are not allowed, for example b'i00e'; {0}; {1}")]
58 LeadingZerosInIntegersNotAllowed(ReadContext, WriteContext),
59
60 #[error("Invalid string length byte, expected a digit; {0}; {1}")]
66 InvalidStringLengthByte(ReadContext, WriteContext),
67
68 #[error("Unexpected end of input parsing string length; {0}; {1}")]
72 UnexpectedEndOfInputParsingStringLength(ReadContext, WriteContext),
73
74 #[error("Unexpected end of input parsing string value; {0}; {1}")]
78 UnexpectedEndOfInputParsingStringValue(ReadContext, WriteContext),
79
80 #[error(
83 "Unexpected end of input parsing list. Expecting first list item or list end; {0}; {1}"
84 )]
85 UnexpectedEndOfInputExpectingFirstListItemOrEnd(ReadContext, WriteContext),
86
87 #[error("Unexpected end of input parsing list. Expecting next list item; {0}; {1}")]
89 UnexpectedEndOfInputExpectingNextListItem(ReadContext, WriteContext),
90
91 #[error("Unexpected end of input parsing dictionary. Expecting first dictionary field or dictionary end; {0}; {1}")]
94 UnexpectedEndOfInputExpectingFirstDictFieldOrEnd(ReadContext, WriteContext),
95
96 #[error(
98 "Unexpected end of input parsing dictionary. Expecting dictionary field value; {0}; {1}"
99 )]
100 UnexpectedEndOfInputExpectingDictFieldValue(ReadContext, WriteContext),
101
102 #[error(
104 "Unexpected end of input parsing dictionary. Expecting dictionary field key or end; {0}; {1}"
105 )]
106 UnexpectedEndOfInputExpectingDictFieldKeyOrEnd(ReadContext, WriteContext),
107
108 #[error("Unexpected end of dictionary. Premature end of dictionary; {0}; {1}")]
110 PrematureEndOfDict(ReadContext, WriteContext),
111
112 #[error("Expected string for dictionary field key, but got: {0}, {1}")]
114 ExpectedStringForDictKeyGot(BencodeType, ReadContext, WriteContext),
115
116 #[error(
119 "Unexpected end of list or dict. No matching start for the list or dict end: {0}, {1}"
120 )]
121 NoMatchingStartForListOrDictEnd(ReadContext, WriteContext),
122}
123
124#[derive(Debug)]
126pub struct ReadContext {
127 pub byte: Option<u8>,
129
130 pub pos: u64,
132
133 pub latest_bytes: Vec<u8>,
135}
136
137impl fmt::Display for ReadContext {
138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139 write!(f, "read context:")?;
140
141 match self.byte {
142 None => {}
143 Some(byte) => write!(f, " byte `{}` (char: `{}`),", byte, byte as char)?,
144 }
145
146 write!(
147 f,
148 " input pos {}, latest input bytes dump: {:?}",
149 self.pos, self.latest_bytes
150 )?;
151
152 if let Ok(utf8_string) = str::from_utf8(&self.latest_bytes) {
153 write!(f, " (UTF-8 string: `{utf8_string}`)")?;
154 }
155
156 Ok(())
157 }
158}
159
160#[derive(Debug)]
162pub struct WriteContext {
163 pub byte: Option<u8>,
165
166 pub pos: u64,
168
169 pub latest_bytes: Vec<u8>,
171}
172
173impl fmt::Display for WriteContext {
174 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175 write!(f, "write context:")?;
176
177 match self.byte {
178 None => {}
179 Some(byte) => write!(f, " byte `{}` (char: `{}`),", byte, byte as char)?,
180 }
181
182 write!(
183 f,
184 " output pos {}, latest output bytes dump: {:?}",
185 self.pos, self.latest_bytes
186 )?;
187
188 if let Ok(utf8_string) = str::from_utf8(&self.latest_bytes) {
189 write!(f, " (UTF-8 string: `{utf8_string}`)")?;
190 }
191
192 Ok(())
193 }
194}
195
196#[cfg(test)]
197mod tests {
198
199 mod for_read_context {
200 use crate::parsers::error::ReadContext;
201
202 #[test]
203 fn it_should_display_the_read_context() {
204 let read_context = ReadContext {
205 byte: Some(b'a'),
206 pos: 10,
207 latest_bytes: vec![b'a', b'b', b'c'],
208 };
209
210 assert_eq!( read_context.to_string(),"read context: byte `97` (char: `a`), input pos 10, latest input bytes dump: [97, 98, 99] (UTF-8 string: `abc`)");
211 }
212
213 #[test]
214 fn it_should_not_display_the_byte_if_it_is_none() {
215 let read_context = ReadContext {
216 byte: None,
217 pos: 10,
218 latest_bytes: vec![b'a', b'b', b'c'],
219 };
220
221 assert_eq!(read_context.to_string(), "read context: input pos 10, latest input bytes dump: [97, 98, 99] (UTF-8 string: `abc`)");
222 }
223
224 #[test]
225 fn it_should_not_display_the_latest_bytes_as_string_if_it_is_not_a_valid_string() {
226 let read_context = ReadContext {
227 byte: None,
228 pos: 10,
229 latest_bytes: vec![b'\xFF', b'\xFE'],
230 };
231
232 assert_eq!(
233 read_context.to_string(),
234 "read context: input pos 10, latest input bytes dump: [255, 254]"
235 );
236 }
237 }
238
239 mod for_write_context {
240 use crate::parsers::error::WriteContext;
241
242 #[test]
243 fn it_should_display_the_read_context() {
244 let read_context = WriteContext {
245 byte: Some(b'a'),
246 pos: 10,
247 latest_bytes: vec![b'a', b'b', b'c'],
248 };
249
250 assert_eq!( read_context.to_string(),"write context: byte `97` (char: `a`), output pos 10, latest output bytes dump: [97, 98, 99] (UTF-8 string: `abc`)");
251 }
252
253 #[test]
254 fn it_should_not_display_the_byte_if_it_is_none() {
255 let read_context = WriteContext {
256 byte: None,
257 pos: 10,
258 latest_bytes: vec![b'a', b'b', b'c'],
259 };
260
261 assert_eq!(read_context.to_string(), "write context: output pos 10, latest output bytes dump: [97, 98, 99] (UTF-8 string: `abc`)");
262 }
263
264 #[test]
265 fn it_should_not_display_the_latest_bytes_as_string_if_it_is_not_a_valid_string() {
266 let read_context = WriteContext {
267 byte: None,
268 pos: 10,
269 latest_bytes: vec![b'\xFF', b'\xFE'],
270 };
271
272 assert_eq!(
273 read_context.to_string(),
274 "write context: output pos 10, latest output bytes dump: [255, 254]"
275 );
276 }
277 }
278}