diff --git a/Cargo.lock b/Cargo.lock index bf6597b7..8fa671cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,12 +11,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "anstyle" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bb0ec944fe041c41e32dcd60b49327fcb7ff761433838878cc97ddecf16db7" - [[package]] name = "anyhow" version = "1.0.66" @@ -37,6 +31,17 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -175,6 +180,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "hermit-abi" version = "0.2.6" @@ -188,15 +202,15 @@ dependencies = [ name = "hexyl" version = "0.11.0" dependencies = [ - "anstyle", "anyhow", "assert_cmd", "clap", "const_format", - "is-terminal", "libc", + "owo-colors", "predicates", "pretty_assertions", + "supports-color", "terminal_size", "thiserror", ] @@ -217,12 +231,18 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "io-lifetimes", "rustix", "windows-sys", ] +[[package]] +name = "is_ci" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb" + [[package]] name = "itertools" version = "0.10.5" @@ -286,6 +306,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + [[package]] name = "predicates" version = "2.1.4" @@ -371,9 +397,9 @@ checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "rustix" -version = "0.36.4" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb93e85278e08bb5788653183213d3a60fc242b10cb9be96586f5a73dcb67c23" +checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588" dependencies = [ "bitflags", "errno", @@ -395,6 +421,16 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "supports-color" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ba6faf2ca7ee42fdd458f4347ae0a9bd6bcc445ad7cb57ad82b383f18870d6f" +dependencies = [ + "atty", + "is_ci", +] + [[package]] name = "syn" version = "1.0.105" diff --git a/Cargo.toml b/Cargo.toml index f0c27696..b4252d19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,11 +11,11 @@ version = "0.11.0" edition = "2021" [dependencies] -anstyle = "0.2" anyhow = "1.0" const_format = "0.2" -is-terminal = "0.4" libc = "0.2" +owo-colors = "3" +supports-color = "1" thiserror = "1.0" terminal_size = "0.2" diff --git a/src/bin/hexyl.rs b/src/bin/hexyl.rs index c91d3a41..872df048 100644 --- a/src/bin/hexyl.rs +++ b/src/bin/hexyl.rs @@ -9,8 +9,6 @@ use std::num::{NonZeroI64, NonZeroU64, NonZeroU8}; use clap::builder::ArgPredicate; use clap::{crate_name, crate_version, Arg, ArgAction, ColorChoice, Command}; -use is_terminal::IsTerminal; - use anyhow::{anyhow, Context, Result}; use const_format::formatcp; @@ -296,8 +294,10 @@ fn run() -> Result<()> { let show_color = match matches.get_one::("color").map(String::as_ref) { Some("never") => false, - Some("auto") => std::io::stdout().is_terminal(), - _ => true, + Some("always") => true, + _ => supports_color::on(supports_color::Stream::Stdout) + .map(|level| level.has_basic) + .unwrap_or(false), }; let border_style = match matches.get_one::("border").map(String::as_ref) { @@ -453,7 +453,7 @@ fn main() { std::process::exit(0); } } - eprintln!("Error: {:?}", err); + eprintln!("Error: {err:?}"); std::process::exit(1); } } diff --git a/src/lib.rs b/src/lib.rs index a9a4c5ae..62d01ced 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,7 @@ pub use input::*; use std::io::{self, BufReader, Read, Write}; -use anstyle::{AnsiColor, Reset}; +use owo_colors::{colors, Color}; pub enum Base { Binary, @@ -13,24 +13,13 @@ pub enum Base { Hexadecimal, } -#[derive(PartialEq, Eq)] -#[repr(usize)] -enum ByteColor { - Null = 0, - Offset = 1, - AsciiPrintable = 2, - AsciiWhitespace = 3, - AsciiOther = 4, - NonAscii = 5, - Reset = 6, -} - -const COLOR_NULL: AnsiColor = AnsiColor::BrightBlack; -const COLOR_OFFSET: AnsiColor = AnsiColor::BrightBlack; -const COLOR_ASCII_PRINTABLE: AnsiColor = AnsiColor::Cyan; -const COLOR_ASCII_WHITESPACE: AnsiColor = AnsiColor::Green; -const COLOR_ASCII_OTHER: AnsiColor = AnsiColor::Magenta; -const COLOR_NONASCII: AnsiColor = AnsiColor::Yellow; +const COLOR_NULL: &[u8] = colors::BrightBlack::ANSI_FG.as_bytes(); +const COLOR_OFFSET: &[u8] = colors::BrightBlack::ANSI_FG.as_bytes(); +const COLOR_ASCII_PRINTABLE: &[u8] = colors::Cyan::ANSI_FG.as_bytes(); +const COLOR_ASCII_WHITESPACE: &[u8] = colors::Green::ANSI_FG.as_bytes(); +const COLOR_ASCII_OTHER: &[u8] = colors::Green::ANSI_FG.as_bytes(); +const COLOR_NONASCII: &[u8] = colors::Yellow::ANSI_FG.as_bytes(); +const COLOR_RESET: &[u8] = colors::Default::ANSI_FG.as_bytes(); pub enum ByteCategory { Null, @@ -66,15 +55,15 @@ impl Byte { } } - fn color(self) -> ByteColor { + fn color(self) -> &'static [u8] { use crate::ByteCategory::*; match self.category() { - Null => ByteColor::Null, - AsciiPrintable => ByteColor::AsciiPrintable, - AsciiWhitespace => ByteColor::AsciiWhitespace, - AsciiOther => ByteColor::AsciiOther, - NonAscii => ByteColor::NonAscii, + Null => COLOR_NULL, + AsciiPrintable => COLOR_ASCII_PRINTABLE, + AsciiWhitespace => COLOR_ASCII_WHITESPACE, + AsciiOther => COLOR_ASCII_OTHER, + NonAscii => COLOR_NONASCII, } } @@ -250,14 +239,12 @@ pub struct Printer<'a, Writer: Write> { show_char_panel: bool, show_position_panel: bool, show_color: bool, - colors: Vec, - curr_color: Option, + curr_color: Option<&'static [u8]>, border_style: BorderStyle, byte_hex_panel: Vec, byte_char_panel: Vec, // same as previous but in Fixed(242) gray color, for position panel byte_hex_panel_g: Vec, - byte_char_panel_g: Vec, squeezer: Squeezer, display_offset: u64, /// The number of panels to draw. @@ -289,15 +276,6 @@ impl<'a, Writer: Write> Printer<'a, Writer> { show_position_panel, show_color, curr_color: None, - colors: vec![ - COLOR_NULL.render_fg().to_string(), - COLOR_OFFSET.render_fg().to_string(), - COLOR_ASCII_PRINTABLE.render_fg().to_string(), - COLOR_ASCII_WHITESPACE.render_fg().to_string(), - COLOR_ASCII_OTHER.render_fg().to_string(), - COLOR_NONASCII.render_fg().to_string(), - Reset.render().to_string(), - ], border_style, byte_hex_panel: (0u8..=u8::MAX) .map(|i| match base { @@ -311,9 +289,6 @@ impl<'a, Writer: Write> Printer<'a, Writer> { .map(|i| format!("{}", Byte(i).as_char())) .collect(), byte_hex_panel_g: (0u8..=u8::MAX).map(|i| format!("{i:02x}")).collect(), - byte_char_panel_g: (0u8..=u8::MAX) - .map(|i| format!("{}", Byte(i).as_char())) - .collect(), squeezer: if use_squeeze { Squeezer::Ignore } else { @@ -354,27 +329,27 @@ impl<'a, Writer: Write> Printer<'a, Writer> { let h_repeat = h.to_string().repeat(self.panel_sz()); if self.show_position_panel { - write!(self.writer, "{l}{h8}{c}", l = l, c = c, h8 = h8).ok(); + write!(self.writer, "{l}{h8}{c}")?; } else { - write!(self.writer, "{}", l).ok(); + write!(self.writer, "{l}")?; } for _ in 0..self.panels - 1 { - write!(self.writer, "{h_repeat}{c}", h_repeat = h_repeat, c = c).ok(); + write!(self.writer, "{h_repeat}{c}")?; } if self.show_char_panel { - write!(self.writer, "{h_repeat}{c}", h_repeat = h_repeat, c = c).ok(); + write!(self.writer, "{h_repeat}{c}")?; } else { - write!(self.writer, "{h_repeat}", h_repeat = h_repeat).ok(); + write!(self.writer, "{h_repeat}")?; } if self.show_char_panel { for _ in 0..self.panels - 1 { - write!(self.writer, "{h8}{c}", h8 = h8, c = c).ok(); + write!(self.writer, "{h8}{c}")?; } - writeln!(self.writer, "{h8}{r}", h8 = h8, r = r).ok(); + writeln!(self.writer, "{h8}{r}")?; } else { - writeln!(self.writer, "{r}", r = r).ok(); + writeln!(self.writer, "{r}")?; } Ok(()) @@ -402,17 +377,15 @@ impl<'a, Writer: Write> Printer<'a, Writer> { .as_bytes(), )?; if self.show_color { - self.writer - .write_all(self.colors[ByteColor::Offset as usize].as_bytes())?; + self.writer.write_all(COLOR_OFFSET)?; } if self.show_position_panel { match self.squeezer { Squeezer::Print => { self.writer - .write_all(self.byte_char_panel_g[b'*' as usize].as_bytes())?; + .write_all(self.byte_char_panel[b'*' as usize].as_bytes())?; if self.show_color { - self.writer - .write_all(self.colors[ByteColor::Reset as usize].as_bytes())?; + self.writer.write_all(COLOR_RESET)?; } self.writer.write_all(b" ")?; } @@ -427,8 +400,7 @@ impl<'a, Writer: Write> Printer<'a, Writer> { .write_all(self.byte_hex_panel_g[byte as usize].as_bytes())?; } if self.show_color { - self.writer - .write_all(self.colors[ByteColor::Reset as usize].as_bytes())?; + self.writer.write_all(COLOR_RESET)?; } } } @@ -448,8 +420,7 @@ impl<'a, Writer: Write> Printer<'a, Writer> { Squeezer::Ignore | Squeezer::Disabled => { if let Some(&b) = self.line_buf.get(i as usize) { if self.show_color && self.curr_color != Some(Byte(b).color()) { - self.writer - .write_all(self.colors[Byte(b).color() as usize].as_bytes())?; + self.writer.write_all(Byte(b).color())?; self.curr_color = Some(Byte(b).color()); } self.writer @@ -461,8 +432,7 @@ impl<'a, Writer: Write> Printer<'a, Writer> { } if i == 8 * self.panels - 1 { if self.show_color { - self.writer - .write_all(self.colors[ByteColor::Reset as usize].as_bytes())?; + self.writer.write_all(COLOR_RESET)?; self.curr_color = None; } self.writer.write_all( @@ -473,8 +443,7 @@ impl<'a, Writer: Write> Printer<'a, Writer> { )?; } else if i % 8 == 7 { if self.show_color { - self.writer - .write_all(self.colors[ByteColor::Reset as usize].as_bytes())?; + self.writer.write_all(COLOR_RESET)?; self.curr_color = None; } self.writer.write_all( @@ -500,19 +469,15 @@ impl<'a, Writer: Write> Printer<'a, Writer> { Squeezer::Print => { if !self.show_position_panel && i == 0 { if self.show_color { - self.writer - .write_all(self.colors[ByteColor::Offset as usize].as_bytes())?; + self.writer.write_all(COLOR_OFFSET)?; } self.writer - .write_all(self.byte_char_panel_g[b'*' as usize].as_bytes())?; + .write_all(self.byte_char_panel[b'*' as usize].as_bytes())?; if self.show_color { - self.writer - .write_all(self.colors[ByteColor::Reset as usize].as_bytes())?; - } - } else { - if i % (self.group_size as usize) == 0 { - self.writer.write_all(b" ")?; + self.writer.write_all(COLOR_RESET)?; } + } else if i % (self.group_size as usize) == 0 { + self.writer.write_all(b" ")?; } for _ in 0..self.base_digits { self.writer.write_all(b" ")?; @@ -524,8 +489,7 @@ impl<'a, Writer: Write> Printer<'a, Writer> { self.writer.write_all(b" ")?; } if self.show_color && self.curr_color != Some(Byte(b).color()) { - self.writer - .write_all(self.colors[Byte(b).color() as usize].as_bytes())?; + self.writer.write_all(Byte(b).color())?; self.curr_color = Some(Byte(b).color()); } self.writer @@ -536,8 +500,7 @@ impl<'a, Writer: Write> Printer<'a, Writer> { if i % 8 == 7 { if self.show_color { self.curr_color = None; - self.writer - .write_all(self.colors[ByteColor::Reset as usize].as_bytes())?; + self.writer.write_all(COLOR_RESET)?; } self.writer.write_all(b" ")?; // byte is last in last panel