diff --git a/src/main.rs b/src/main.rs index bb92564..c90a220 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,9 @@ use clap::{Arg, Command}; use std::fs::File; use std::io::{self, Read, Write}; -use torrust_bencode2json::parsers::BencodeParser; +use torrust_bencode2json::parsers::{self, BencodeParser}; -fn main() -> Result<(), torrust_bencode2json::rw::error::Error> { +fn main() -> Result<(), parsers::error::Error> { let matches = Command::new("torrust-bencode2json") .version("0.1.0") .author("Torrust Organization") diff --git a/src/parsers/error.rs b/src/parsers/error.rs index 831b2aa..9d8a88c 100644 --- a/src/parsers/error.rs +++ b/src/parsers/error.rs @@ -6,5 +6,14 @@ use crate::rw; #[derive(Debug, Error)] pub enum Error { #[error("R/W error: {0}")] - Rw(#[from] rw::Error), + Rw(#[from] rw::error::Error), + + #[error("Unexpected byte parsing integer: {0}")] + UnexpectedByteParsingInteger(u8), // todo: add input byte pos and dump previous bytes +} + +impl From for Error { + fn from(err: std::io::Error) -> Self { + Self::Rw(err.into()) + } } diff --git a/src/parsers/integer.rs b/src/parsers/integer.rs index 9397606..28eef4b 100644 --- a/src/parsers/integer.rs +++ b/src/parsers/integer.rs @@ -3,7 +3,9 @@ //! It reads bencoded bytes from the input and writes JSON bytes to the output. use std::io::Read; -use crate::rw::{byte_reader::ByteReader, error::Error, writer::Writer}; +use crate::rw::{byte_reader::ByteReader, writer::Writer}; + +use super::error::Error; // code-review: state machine to check state transitions (runtime or compile time)? @@ -37,7 +39,7 @@ pub fn parse( loop { let byte = match reader.read_byte() { Ok(byte) => byte, - Err(err) => return Err(err), + Err(err) => return Err(err.into()), }; let char = byte as char; @@ -51,7 +53,7 @@ pub fn parse( state = StateExpecting::DigitAfterSign; writer.write_byte(byte)?; } else { - panic!("invalid integer"); + return Err(Error::UnexpectedByteParsingInteger(byte)); } } } diff --git a/src/parsers/mod.rs b/src/parsers/mod.rs index b211919..206a303 100644 --- a/src/parsers/mod.rs +++ b/src/parsers/mod.rs @@ -11,7 +11,7 @@ use std::{ use stack::{Stack, State}; use crate::rw::{ - byte_reader::ByteReader, byte_writer::ByteWriter, error::Error, string_writer::StringWriter, + self, byte_reader::ByteReader, byte_writer::ByteWriter, string_writer::StringWriter, writer::Writer, }; @@ -53,7 +53,7 @@ impl BencodeParser { /// /// Will panic if receives a byte that isn't a valid begin or end of a /// bencoded type: integer, string, list or dictionary. - pub fn write_str(&mut self, writer: W) -> Result<(), crate::rw::error::Error> { + pub fn write_str(&mut self, writer: W) -> Result<(), error::Error> { let mut writer = StringWriter::new(writer); self.parse(&mut writer) } @@ -70,7 +70,7 @@ impl BencodeParser { /// /// Will panic if receives a byte that isn't a valid begin or end of a /// bencoded type: integer, string, list or dictionary. - pub fn write_bytes(&mut self, writer: W) -> Result<(), crate::rw::error::Error> { + pub fn write_bytes(&mut self, writer: W) -> Result<(), error::Error> { let mut writer = ByteWriter::new(writer); self.parse(&mut writer) } @@ -87,15 +87,17 @@ impl BencodeParser { /// /// Will panic if receives a byte that isn't a valid begin or end of a /// bencoded type: integer, string, list or dictionary. - fn parse(&mut self, writer: &mut W) -> Result<(), crate::rw::error::Error> { + fn parse(&mut self, writer: &mut W) -> Result<(), error::Error> { loop { let byte = match self.byte_reader.read_byte() { Ok(byte) => byte, Err(err) => match err { - Error::Io(ref io_error) if io_error.kind() == io::ErrorKind::UnexpectedEof => { + rw::error::Error::Io(ref io_error) + if io_error.kind() == io::ErrorKind::UnexpectedEof => + { break; } - Error::Io(_) | Error::Fmt(_) => return Err(err), + rw::error::Error::Io(_) | rw::error::Error::Fmt(_) => return Err(err.into()), }, }; @@ -235,7 +237,7 @@ impl BencodeParser { #[cfg(test)] mod tests { - use super::BencodeParser; + use super::{error, BencodeParser}; #[test] fn it_should_allow_writing_to_a_byte_vector() { @@ -292,7 +294,7 @@ mod tests { } mod integers { - use crate::parsers::tests::to_json; + use crate::parsers::tests::{parse, to_json}; #[test] fn zero() { @@ -316,6 +318,23 @@ mod tests { // todo: all encodings with a leading zero, such as i03e, are invalid, other // than i0e, which of course corresponds to 0. + + #[test] + fn it_should_fail_when_it_finds_an_invalid_byte() { + let int_with_invalid_byte = b"iae"; + + match parse(int_with_invalid_byte) { + Ok(_output) => panic!( + "expected error parsing invalid byte in integer: {int_with_invalid_byte:?}" + ), + Err(err) => match err { + crate::parsers::error::Error::Rw(_err) => panic!("invalid error"), + crate::parsers::error::Error::UnexpectedByteParsingInteger(byte) => { + assert_eq!(byte, b'a'); + } + }, + } + } } mod strings { @@ -611,4 +630,16 @@ mod tests { output } + + /// Wrapper to easily use the parser in tests + fn parse(input_buffer: &[u8]) -> Result { + let mut output = String::new(); + + let mut parser = BencodeParser::new(input_buffer); + + match parser.write_str(&mut output) { + Ok(()) => Ok(output), + Err(err) => Err(err), + } + } } diff --git a/src/rw/mod.rs b/src/rw/mod.rs index 2a6a59e..a7bf9e5 100644 --- a/src/rw/mod.rs +++ b/src/rw/mod.rs @@ -4,5 +4,3 @@ pub mod byte_writer; pub mod error; pub mod string_writer; pub mod writer; - -pub type Error = error::Error;