Skip to content

Commit

Permalink
feat: capture interger parsing error
Browse files Browse the repository at this point in the history
  • Loading branch information
josecelano committed Oct 7, 2024
1 parent a09ff08 commit a26aede
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 16 deletions.
4 changes: 2 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -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")
Expand Down
11 changes: 10 additions & 1 deletion src/parsers/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Self::Rw(err.into())
}
}
8 changes: 5 additions & 3 deletions src/parsers/integer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?

Expand Down Expand Up @@ -37,7 +39,7 @@ pub fn parse<R: Read, W: Writer>(
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;
Expand All @@ -51,7 +53,7 @@ pub fn parse<R: Read, W: Writer>(
state = StateExpecting::DigitAfterSign;
writer.write_byte(byte)?;
} else {
panic!("invalid integer");
return Err(Error::UnexpectedByteParsingInteger(byte));
}
}
}
47 changes: 39 additions & 8 deletions src/parsers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down Expand Up @@ -53,7 +53,7 @@ impl<R: Read> BencodeParser<R> {
///
/// 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<W: FmtWrite>(&mut self, writer: W) -> Result<(), crate::rw::error::Error> {
pub fn write_str<W: FmtWrite>(&mut self, writer: W) -> Result<(), error::Error> {
let mut writer = StringWriter::new(writer);
self.parse(&mut writer)
}
Expand All @@ -70,7 +70,7 @@ impl<R: Read> BencodeParser<R> {
///
/// 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<W: IoWrite>(&mut self, writer: W) -> Result<(), crate::rw::error::Error> {
pub fn write_bytes<W: IoWrite>(&mut self, writer: W) -> Result<(), error::Error> {
let mut writer = ByteWriter::new(writer);
self.parse(&mut writer)
}
Expand All @@ -87,15 +87,17 @@ impl<R: Read> BencodeParser<R> {
///
/// 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<W: Writer>(&mut self, writer: &mut W) -> Result<(), crate::rw::error::Error> {
fn parse<W: Writer>(&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()),
},
};

Expand Down Expand Up @@ -235,7 +237,7 @@ impl<R: Read> BencodeParser<R> {
#[cfg(test)]
mod tests {

use super::BencodeParser;
use super::{error, BencodeParser};

#[test]
fn it_should_allow_writing_to_a_byte_vector() {
Expand Down Expand Up @@ -292,7 +294,7 @@ mod tests {
}

mod integers {
use crate::parsers::tests::to_json;
use crate::parsers::tests::{parse, to_json};

#[test]
fn zero() {
Expand All @@ -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 {
Expand Down Expand Up @@ -611,4 +630,16 @@ mod tests {

output
}

/// Wrapper to easily use the parser in tests
fn parse(input_buffer: &[u8]) -> Result<String, error::Error> {
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),
}
}
}
2 changes: 0 additions & 2 deletions src/rw/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,3 @@ pub mod byte_writer;
pub mod error;
pub mod string_writer;
pub mod writer;

pub type Error = error::Error;

0 comments on commit a26aede

Please sign in to comment.