Skip to content

Commit

Permalink
refactor: extract byte_writer
Browse files Browse the repository at this point in the history
  • Loading branch information
josecelano committed Sep 24, 2024
1 parent 3876371 commit 14211e2
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 53 deletions.
68 changes: 68 additions & 0 deletions src/byte_writer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use core::str;
use std::io::{self, Write};

/// A writer that writes bytes to an output.
///
/// It's wrapper of a basic writer with extra functionality.
pub struct ByteWriter<W: Write> {
/// Number of bytes write to the output.
pub output_byte_counter: u64,

/// It optionally captures the output.
pub opt_captured_output: Option<String>,

writer: W,
}

impl<W: Write> ByteWriter<W> {
pub fn new(writer: W) -> Self {
Self {
output_byte_counter: 0,
opt_captured_output: Some(String::new()),
writer,
}
}

/// It writes one byte to the output (stdout or file).
///
/// # Errors
///
/// Will return an error if it can't write the byte to the output.
pub fn write_byte(&mut self, byte: u8) -> io::Result<()> {
let bytes = [byte];

self.writer.write_all(&bytes)?;

self.output_byte_counter += 1;

if let Some(ref mut captured_output) = self.opt_captured_output {
captured_output.push(byte as char);
}

Ok(())
}

/// It writes a string to the output (stdout or file).
///
/// # Errors
///
/// Will return an error if it can't write the string (as bytes) to the output.
pub fn write_str(&mut self, value: &str) -> io::Result<()> {
self.writer.write_all(value.as_bytes())?;

self.output_byte_counter += value.as_bytes().len() as u64;

if let Some(ref mut captured_output) = self.opt_captured_output {
captured_output.push_str(value);
}

Ok(())
}

/// It prints the captured output is enabled.
pub fn print_captured_output(&self) {
if let Some(output) = &self.opt_captured_output {
println!("output: {output}");
}
}
}
71 changes: 18 additions & 53 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,18 @@ use std::io::{self, Read, Write};
use std::str::{self};

use byte_reader::ByteReader;
use byte_writer::ByteWriter;
use stack::{Stack, State};

pub mod byte_reader;
pub mod byte_writer;
pub mod stack;

pub struct BencodeParser<R: Read, W: Write> {
pub debug: bool,
pub iter: u64,

byte_reader: ByteReader<R>,

writer: W,
output_byte_counter: u64,
opt_captured_output: Option<String>,

byte_writer: ByteWriter<W>,
stack: Stack,
}

Expand Down Expand Up @@ -107,13 +104,8 @@ impl<R: Read, W: Write> BencodeParser<R, W> {
BencodeParser {
debug: false, // todo: use tracing crate
iter: 1,

byte_reader: ByteReader::new(reader),

writer,
output_byte_counter: 0,
opt_captured_output: Some(String::new()),

byte_writer: ByteWriter::new(writer),
stack: Stack::default(),
}
}
Expand Down Expand Up @@ -159,13 +151,13 @@ impl<R: Read, W: Write> BencodeParser<R, W> {
b'l' => {
// Begin of list
self.begin_bencoded_value()?;
self.write_byte(b'[')?;
self.byte_writer.write_byte(b'[')?;
self.stack.push(State::ExpectingFirstItemOrEnd);
}
b'd' => {
// Begin of dictionary
self.begin_bencoded_value()?;
self.write_byte(b'{')?;
self.byte_writer.write_byte(b'{')?;
self.stack.push(State::ExpectingFirstFieldOrEnd);
}
b'e' => {
Expand All @@ -181,10 +173,7 @@ impl<R: Read, W: Write> BencodeParser<R, W> {
println!("stack: {}", self.stack);
//println!("string_parser: {:#?}", self.string_parser);
self.byte_reader.print_captured_input();
match &self.opt_captured_output {
Some(output) => println!("output: {output}"),
None => {}
}
self.byte_writer.print_captured_output();
println!();
}

Expand Down Expand Up @@ -214,20 +203,20 @@ impl<R: Read, W: Write> BencodeParser<R, W> {
}
State::ExpectingNextItem => {
// Items separator
self.write_byte(b',')?;
self.byte_writer.write_byte(b',')?;
}
// Dictionary
State::ExpectingFirstFieldOrEnd => {
self.stack.swap_top(State::ExpectingFieldValue);
}
State::ExpectingFieldValue => {
// Key/Value separator
self.write_byte(b':')?;
self.byte_writer.write_byte(b':')?;
self.stack.swap_top(State::ExpectingFieldKey);
}
State::ExpectingFieldKey => {
// Field separator
self.write_byte(b',')?;
self.byte_writer.write_byte(b',')?;
self.stack.swap_top(State::ExpectingFieldValue);
}
}
Expand All @@ -251,11 +240,11 @@ impl<R: Read, W: Write> BencodeParser<R, W> {
pub fn end_bencoded_value(&mut self) -> io::Result<()> {
match self.stack.peek() {
State::ExpectingFirstItemOrEnd | State::ExpectingNextItem => {
self.write_byte(b']')?;
self.byte_writer.write_byte(b']')?;
self.stack.pop();
}
State::ExpectingFirstFieldOrEnd | State::ExpectingFieldKey => {
self.write_byte(b'}')?;
self.byte_writer.write_byte(b'}')?;
}
State::ExpectingFieldValue | State::Initial => {
// todo: pass the type of value (list or dict) to customize the error message
Expand Down Expand Up @@ -291,12 +280,12 @@ impl<R: Read, W: Write> BencodeParser<R, W> {

if char.is_ascii_digit() {
st = 2;
self.write_byte(byte)?;
self.byte_writer.write_byte(byte)?;
} else if char == 'e' && st == 2 {
return Ok(());
} else if char == '-' && st == 0 {
st = 1;
self.write_byte(byte)?;
self.byte_writer.write_byte(byte)?;
} else {
panic!("invalid integer");
}
Expand Down Expand Up @@ -349,39 +338,15 @@ impl<R: Read, W: Write> BencodeParser<R, W> {
// todo: escape '"' and '\\' with '\\';
}

self.write_str(&string_parser.json())?;
self.byte_writer.write_str(&string_parser.json())?;

//println!("string_parser {string_parser:#?}");

Ok(())
}

/// It writes one byte to the output (stdout or file).
fn write_byte(&mut self, byte: u8) -> io::Result<()> {
let bytes = [byte];

self.writer.write_all(&bytes)?;

self.output_byte_counter += 1;

if let Some(ref mut captured_output) = self.opt_captured_output {
captured_output.push(byte as char);
}

Ok(())
}

/// It writes a string to the output (stdout or file).
fn write_str(&mut self, value: &str) -> io::Result<()> {
self.writer.write_all(value.as_bytes())?;

self.output_byte_counter += value.as_bytes().len() as u64;

if let Some(ref mut captured_output) = self.opt_captured_output {
captured_output.push_str(value);
}

Ok(())
pub fn opt_captured_output(&self) -> Option<String> {
self.byte_writer.opt_captured_output.clone()
}
}

Expand All @@ -396,7 +361,7 @@ mod tests {
let mut parser = BencodeParser::new(bytes, Box::new(io::stdout()));
parser.parse().expect("bencoded to JSON conversion failed");

match parser.opt_captured_output {
match parser.opt_captured_output() {
Some(captured_output) => captured_output,
None => panic!(
"capturing output is not enabled in parser, please enable it to run the tests"
Expand Down

0 comments on commit 14211e2

Please sign in to comment.