run: factor out mainloop and add tests

This commit is contained in:
Tom Milligan authored and David Peter committed 2019-07-17 19:31:37 +02:00
1 parent 4c48eedb18
commit 622a084ea6
2 files changed
+86 -32

No files matched your search

+1 -31
View File
@@ -15,8 +15,6 @@ use atty::Stream;
use hexyl::{BorderStyle, Printer};
const BUFFER_SIZE: usize = 256;
fn run() -> Result<(), Box<dyn std::error::Error>> {
let app = App::new(crate_name!())
.setting(AppSettings::ColorAuto)
@@ -121,35 +119,7 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
let mut stdout_lock = stdout.lock();
let mut printer = Printer::new(&mut stdout_lock, show_color, border_style, squeeze);
let mut buffer = [0; BUFFER_SIZE];
'mainloop: loop {
let size = reader.read(&mut buffer)?;
if size == 0 {
break;
}
if cancelled.load(Ordering::SeqCst) {
eprintln!("hexyl has been cancelled.");
std::process::exit(130); // Set exit code to 128 + SIGINT
}
for b in &buffer[..size] {
let res = printer.print_byte(*b);
if res.is_err() {
// Broken pipe
break 'mainloop;
}
}
}
// Finish last line
printer.print_textline().ok();
if !printer.header_was_printed() {
printer.header();
}
printer.footer();
printer.print_all(&mut reader, Some(cancelled))?;
Ok(())
}
+85 -1
View File
@@ -1,12 +1,18 @@
pub mod squeezer;
use std::io::{self, Write};
use std::io::{self, Read, Write};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use ansi_term::Color;
use ansi_term::Color::Fixed;
use squeezer::{SqueezeAction, Squeezer};
type Cancelled = Arc<AtomicBool>;
const BUFFER_SIZE: usize = 256;
const COLOR_NULL: Color = Fixed(242); // grey
const COLOR_OFFSET: Color = Fixed(242); // grey
const COLOR_ASCII_PRINTABLE: Color = Color::Cyan;
@@ -387,4 +393,82 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
pub fn header_was_printed(&self) -> bool {
self.header_was_printed
}
/// Loop through the given `Reader`, printing until the `Reader` buffer
/// is exhausted, or the optional `cancelled` bool is set to true.
pub fn print_all<Reader: Read>(
&mut self,
mut reader: Reader,
canceller: Option<Cancelled>,
) -> Result<(), Box<dyn std::error::Error>> {
let mut buffer = [0; BUFFER_SIZE];
'mainloop: loop {
let size = reader.read(&mut buffer)?;
if size == 0 {
break;
}
if let Some(cancelled) = &canceller {
if cancelled.load(Ordering::SeqCst) {
eprintln!("hexyl has been cancelled.");
std::process::exit(130); // Set exit code to 128 + SIGINT
}
}
for b in &buffer[..size] {
let res = self.print_byte(*b);
if res.is_err() {
// Broken pipe
break 'mainloop;
}
}
}
// Finish last line
self.print_textline().ok();
if !self.header_was_printed() {
self.header();
}
self.footer();
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::io;
use std::str;
use super::*;
fn assert_print_all_output<Reader: Read>(input: Reader, expected_string: String) -> () {
let mut output = vec![];
let mut printer = Printer::new(&mut output, false, BorderStyle::Unicode, true);
printer.print_all(input, None).unwrap();
let actual_string: &str = str::from_utf8(&output).unwrap();
assert_eq!(actual_string, expected_string)
}
#[test]
fn empty_file_passes() {
let input = io::empty();
let expected_string = "┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
".to_owned();
assert_print_all_output(input, expected_string);
}
#[test]
fn short_input_passes() {
let input = io::Cursor::new(b"spam");
let expected_string = "┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│00000000│ 73 70 61 6d ┊ │spam ┊ │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
".to_owned();
assert_print_all_output(input, expected_string);
}
}