diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 0bad9ec0..39d61d9e 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -1,7 +1,7 @@ name: CICD env: - MIN_SUPPORTED_RUST_VERSION: "1.46.0" + MIN_SUPPORTED_RUST_VERSION: "1.50.0" CICD_INTERMEDIATES_DIR: "_cicd-intermediates" on: diff --git a/examples/simple.rs b/examples/simple.rs index 7470d879..2c551cb7 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -12,9 +12,18 @@ fn main() { let mut handle = stdout.lock(); let show_color = true; + let show_char_panel = true; + let show_position_panel = true; let use_squeezing = false; let border_style = BorderStyle::Unicode; - let mut printer = Printer::new(&mut handle, show_color, border_style, use_squeezing); + let mut printer = Printer::new( + &mut handle, + show_color, + show_char_panel, + show_position_panel, + border_style, + use_squeezing, + ); printer.print_all(&input[..]).unwrap(); } diff --git a/src/bin/hexyl.rs b/src/bin/hexyl.rs index 367cc4e7..b16cd822 100644 --- a/src/bin/hexyl.rs +++ b/src/bin/hexyl.rs @@ -104,24 +104,41 @@ fn run() -> Result<(), AnyhowError> { .takes_value(true) .value_name("WHEN") .possible_values(&["always", "auto", "never"]) + .default_value_if("plain", None, "never") .default_value("always") .help( "When to use colors. The auto-mode only displays colors if the output \ goes to an interactive terminal", ), ) + .arg(Arg::with_name("plain").short("p").long("plain").help( + "Display output with --no-characters, --no-position, --border=none, and --color=never.", + )) .arg( Arg::with_name("border") .long("border") .takes_value(true) .value_name("STYLE") .possible_values(&["unicode", "ascii", "none"]) + .default_value_if("plain", None, "none") .default_value("unicode") .help( "Whether to draw a border with Unicode characters, ASCII characters, \ or none at all", ), ) + .arg( + Arg::with_name("no_chars") + .short("C") + .long("no-characters") + .help("Whether to display the character panel on the right."), + ) + .arg( + Arg::with_name("no_position") + .short("P") + .long("no-position") + .help("Whether to display the position panel on the left."), + ) .arg( Arg::with_name("display_offset") .short("o") @@ -237,6 +254,10 @@ fn run() -> Result<(), AnyhowError> { let squeeze = !matches.is_present("nosqueezing"); + let show_char_panel = !matches.is_present("no_chars") && !matches.is_present("plain"); + + let show_position_panel = !matches.is_present("no_position") && !matches.is_present("plain"); + let display_offset: u64 = matches .value_of("display_offset") .map(|s| { @@ -251,7 +272,14 @@ fn run() -> Result<(), AnyhowError> { let stdout = io::stdout(); let mut stdout_lock = stdout.lock(); - let mut printer = Printer::new(&mut stdout_lock, show_color, border_style, squeeze); + let mut printer = Printer::new( + &mut stdout_lock, + show_color, + show_char_panel, + show_position_panel, + border_style, + squeeze, + ); printer.display_offset(skip_offset + display_offset); printer.print_all(&mut reader).map_err(|e| anyhow!(e))?; diff --git a/src/lib.rs b/src/lib.rs index 41859da8..e23bc50a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -146,10 +146,12 @@ pub struct Printer<'a, Writer: Write> { buffer_line: Vec, writer: &'a mut Writer, show_color: bool, + show_char_panel: bool, + show_position_panel: bool, border_style: BorderStyle, header_was_printed: bool, - byte_hex_table: Vec, - byte_char_table: Vec, + byte_hex_panel: Vec, + byte_char_panel: Vec, squeezer: Squeezer, display_offset: u64, } @@ -158,6 +160,8 @@ impl<'a, Writer: Write> Printer<'a, Writer> { pub fn new( writer: &'a mut Writer, show_color: bool, + show_char_panel: bool, + show_position_panel: bool, border_style: BorderStyle, use_squeeze: bool, ) -> Printer<'a, Writer> { @@ -167,9 +171,11 @@ impl<'a, Writer: Write> Printer<'a, Writer> { buffer_line: vec![], writer, show_color, + show_char_panel, + show_position_panel, border_style, header_was_printed: false, - byte_hex_table: (0u8..=u8::max_value()) + byte_hex_panel: (0u8..=u8::max_value()) .map(|i| { let byte_hex = format!("{:02x} ", i); if show_color { @@ -179,16 +185,20 @@ impl<'a, Writer: Write> Printer<'a, Writer> { } }) .collect(), - byte_char_table: (0u8..=u8::max_value()) - .map(|i| { - let byte_char = format!("{}", Byte(i).as_char()); - if show_color { - Byte(i).color().paint(byte_char).to_string() - } else { - byte_char - } + byte_char_panel: show_char_panel + .then(|| { + (0u8..=u8::max_value()) + .map(|i| { + let byte_char = format!("{}", Byte(i).as_char()); + if show_color { + Byte(i).color().paint(byte_char).to_string() + } else { + byte_char + } + }) + .collect() }) - .collect(), + .unwrap_or_default(), squeezer: Squeezer::new(use_squeeze), display_offset: 0, } @@ -199,48 +209,49 @@ impl<'a, Writer: Write> Printer<'a, Writer> { self } - pub fn header(&mut self) { - if let Some(border_elements) = self.border_style.header_elems() { - let h = border_elements.horizontal_line; - let h8 = h.to_string().repeat(8); - let h25 = h.to_string().repeat(25); + fn write_border(&mut self, border_elements: BorderElements) { + let h = border_elements.horizontal_line; + let c = border_elements.column_separator; + let l = border_elements.left_corner; + let r = border_elements.right_corner; + let h8 = h.to_string().repeat(8); + let h25 = h.to_string().repeat(25); - writeln!( - self.writer, - "{l}{h8}{c}{h25}{c}{h25}{c}{h8}{c}{h8}{r}", - l = border_elements.left_corner, - c = border_elements.column_separator, - r = border_elements.right_corner, - h8 = h8, - h25 = h25 - ) - .ok(); + if self.show_position_panel { + write!(self.writer, "{l}{h8}{c}", l = l, c = c, h8 = h8).ok(); + } else { + write!(self.writer, "{}", l).ok(); + } + + write!(self.writer, "{h25}{c}{h25}", c = c, h25 = h25).ok(); + + if self.show_char_panel { + writeln!(self.writer, "{c}{h8}{c}{h8}{r}", c = c, h8 = h8, r = r).ok(); + } else { + writeln!(self.writer, "{r}", r = r).ok(); } } - pub fn footer(&mut self) { - if let Some(border_elements) = self.border_style.footer_elems() { - let h = border_elements.horizontal_line; - let h8 = h.to_string().repeat(8); - let h25 = h.to_string().repeat(25); + pub fn print_header(&mut self) { + if self.header_was_printed { + return; + } + if let Some(e) = self.border_style.header_elems() { + self.write_border(e) + } + self.header_was_printed = true; + } - writeln!( - self.writer, - "{l}{h8}{c}{h25}{c}{h25}{c}{h8}{c}{h8}{r}", - l = border_elements.left_corner, - c = border_elements.column_separator, - r = border_elements.right_corner, - h8 = h8, - h25 = h25 - ) - .ok(); + pub fn print_footer(&mut self) { + if let Some(e) = self.border_style.footer_elems() { + self.write_border(e) } } - fn print_position_indicator(&mut self) { - if !self.header_was_printed { - self.header(); - self.header_was_printed = true; + fn print_position_panel(&mut self) { + if !self.show_position_panel { + write!(&mut self.buffer_line, "{} ", self.border_style.outer_sep()).ok(); + return; } let style = COLOR_OFFSET.normal(); @@ -259,12 +270,58 @@ impl<'a, Writer: Write> Printer<'a, Writer> { ); } + pub fn print_char_panel(&mut self) { + if !self.show_char_panel { + // just write newline if character panel is hidden + writeln!(&mut self.buffer_line).ok(); + return; + } + + let len = self.raw_line.len(); + + let mut idx = 1; + for &b in self.raw_line.iter() { + let _ = write!( + &mut self.buffer_line, + "{}", + self.byte_char_panel[b as usize] + ); + + if idx == 8 { + let _ = write!(&mut self.buffer_line, "{}", self.border_style.inner_sep()); + } + + idx += 1; + } + + if len < 8 { + let _ = writeln!( + &mut self.buffer_line, + "{0:1$}{3}{0:2$}{4}", + "", + 8 - len, + 8, + self.border_style.inner_sep(), + self.border_style.outer_sep(), + ); + } else { + let _ = writeln!( + &mut self.buffer_line, + "{0:1$}{2}", + "", + 16 - len, + self.border_style.outer_sep() + ); + } + } + pub fn print_byte(&mut self, b: u8) -> io::Result<()> { if self.idx % 16 == 1 { - self.print_position_indicator(); + self.print_header(); + self.print_position_panel(); } - write!(&mut self.buffer_line, "{}", self.byte_hex_table[b as usize])?; + write!(&mut self.buffer_line, "{}", self.byte_hex_panel[b as usize])?; self.raw_line.push(b); self.squeezer.process(b, self.idx); @@ -289,7 +346,7 @@ impl<'a, Writer: Write> Printer<'a, Writer> { if len == 0 { if self.squeezer.active() { - self.print_position_indicator(); + self.print_position_panel(); let _ = writeln!( &mut self.buffer_line, "{0:1$}{4}{0:2$}{5}{0:3$}{4}{0:3$}{5}", @@ -327,43 +384,10 @@ impl<'a, Writer: Write> Printer<'a, Writer> { self.border_style.outer_sep() ); } - - let mut idx = 1; - for &b in self.raw_line.iter() { - let _ = write!( - &mut self.buffer_line, - "{}", - self.byte_char_table[b as usize] - ); - - if idx == 8 { - let _ = write!(&mut self.buffer_line, "{}", self.border_style.inner_sep()); - } - - idx += 1; - } - - if len < 8 { - let _ = writeln!( - &mut self.buffer_line, - "{0:1$}{3}{0:2$}{4}", - "", - 8 - len, - 8, - self.border_style.inner_sep(), - self.border_style.outer_sep(), - ); - } else { - let _ = writeln!( - &mut self.buffer_line, - "{0:1$}{2}", - "", - 16 - len, - self.border_style.outer_sep() - ); - } } + self.print_char_panel(); + match squeeze_action { SqueezeAction::Print => { self.buffer_line.clear(); @@ -430,14 +454,22 @@ impl<'a, Writer: Write> Printer<'a, Writer> { self.print_textline().ok(); if !self.header_was_printed() { - self.header(); - writeln!( + self.print_header(); + if self.show_position_panel { + write!(self.writer, "{0:9}", "│").ok(); + } + write!( self.writer, - "│ │ No content to print │ │ │ │" + "{0:2}{1:24}{0}{0:>26}", + "│", "No content to print" ) .ok(); + if self.show_char_panel { + write!(self.writer, "{0:>9}{0:>9}", "│").ok(); + } + writeln!(self.writer).ok(); } - self.footer(); + self.print_footer(); Ok(()) } @@ -450,14 +482,14 @@ mod tests { use super::*; - fn assert_print_all_output(input: Reader, expected_string: String) -> () { + fn assert_print_all_output(input: Reader, expected_string: String) { let mut output = vec![]; - let mut printer = Printer::new(&mut output, false, BorderStyle::Unicode, true); + let mut printer = Printer::new(&mut output, false, true, true, BorderStyle::Unicode, true); printer.print_all(input).unwrap(); let actual_string: &str = str::from_utf8(&output).unwrap(); - assert_eq!(actual_string, expected_string) + assert_eq!(actual_string, expected_string,) } #[test] @@ -497,7 +529,7 @@ mod tests { let mut output = vec![]; let mut printer: Printer> = - Printer::new(&mut output, false, BorderStyle::Unicode, true); + Printer::new(&mut output, false, true, true, BorderStyle::Unicode, true); printer.display_offset(0xdeadbeef); printer.print_all(input).unwrap(); diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index bc718add..df076e5a 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -227,3 +227,47 @@ mod blocksize { .failure(); } } + +mod display_settings { + use super::hexyl; + + #[test] + fn plain() { + hexyl() + .arg("ascii") + .arg("--plain") + .assert() + .success() + .stdout(" 30 31 32 33 34 35 36 37 38 39 61 62 63 64 65 0a \n"); + } + + #[test] + fn no_chars() { + hexyl() + .arg("ascii") + .arg("--no-characters") + .arg("--color=never") + .assert() + .success() + .stdout( + "┌────────┬─────────────────────────┬─────────────────────────┐\n\ + │00000000│ 30 31 32 33 34 35 36 37 ┊ 38 39 61 62 63 64 65 0a │\n\ + └────────┴─────────────────────────┴─────────────────────────┘\n", + ); + } + + #[test] + fn no_position() { + hexyl() + .arg("ascii") + .arg("--no-position") + .arg("--color=never") + .assert() + .success() + .stdout( + "┌─────────────────────────┬─────────────────────────┬────────┬────────┐\n\ + │ 30 31 32 33 34 35 36 37 ┊ 38 39 61 62 63 64 65 0a │01234567┊89abcde_│\n\ + └─────────────────────────┴─────────────────────────┴────────┴────────┘\n", + ); + } +}