From e4e6c6b323b676efa075fd3d0afc5d73808d3d95 Mon Sep 17 00:00:00 2001 From: sharkdp Date: Mon, 5 Nov 2018 22:40:43 +0100 Subject: [PATCH] Colors! --- Cargo.lock | 1 + Cargo.toml | 1 + src/main.rs | 78 +++++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 72 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4813c27..873348b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,6 +40,7 @@ dependencies = [ name = "hexyl" version = "0.1.0" dependencies = [ + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/Cargo.toml b/Cargo.toml index 8c5cfe8..06911e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ repository = "https://github.com/sharkdp/hexyl" version = "0.1.0" [dependencies] +ansi_term = "0.11" [dependencies.clap] version = "2" diff --git a/src/main.rs b/src/main.rs index 0e91f8d..a897655 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,23 +1,83 @@ #[macro_use] extern crate clap; +extern crate ansi_term; use std::fs::File; use std::io::{self, prelude::*, StdoutLock}; use clap::{App, AppSettings, Arg}; +use ansi_term::Colour; +use ansi_term::Colour::RGB; + const BUFFER_SIZE: usize = 64; +const COLOR_NULL: Colour = RGB(117, 113, 94); // grey +const COLOR_ASCII_PRINTABLE: Colour = RGB(102, 217, 239); // cyan +const COLOR_ASCII_WHITESPACE: Colour = RGB(166, 226, 46); // green +const COLOR_ASCII_OTHER: Colour = RGB(249, 38, 114); // magenta +const COLOR_NONASCII: Colour = RGB(253, 151, 31); // orange + +enum ByteCategory { + Null, + AsciiPrintable, + AsciiWhitespace, + AsciiOther, + NonAscii, +} + +#[derive(Copy, Clone)] +struct Byte(u8); + +impl Byte { + fn category(self) -> ByteCategory { + if self.0 == 0x00 { + ByteCategory::Null + } else if self.0.is_ascii_alphanumeric() + || self.0.is_ascii_punctuation() + || self.0.is_ascii_graphic() + { + ByteCategory::AsciiPrintable + } else if self.0.is_ascii_whitespace() { + ByteCategory::AsciiWhitespace + } else if self.0.is_ascii() { + ByteCategory::AsciiOther + } else { + ByteCategory::NonAscii + } + } + + fn color(&self) -> &'static Colour { + use ByteCategory::*; + + match self.category() { + Null => &COLOR_NULL, + AsciiPrintable => &COLOR_ASCII_PRINTABLE, + AsciiWhitespace => &COLOR_ASCII_WHITESPACE, + AsciiOther => &COLOR_ASCII_OTHER, + NonAscii => &COLOR_NONASCII, + } + } + + fn as_char(self) -> char { + use ByteCategory::*; + + match self.category() { + Null => '0', + AsciiPrintable => self.0 as char, + AsciiWhitespace => '.', + AsciiOther => '.', + NonAscii => '.', + } + } +} + struct Printer<'a> { idx: usize, line: Vec, stdout: StdoutLock<'a>, } -fn byte_is_printable(b: u8) -> bool { - b.is_ascii_alphanumeric() || b.is_ascii_punctuation() || b.is_ascii_graphic() -} - impl<'a> Printer<'a> { fn new(stdout: StdoutLock) -> Printer { Printer { @@ -32,7 +92,8 @@ impl<'a> Printer<'a> { write!(self.stdout, " "); } - write!(self.stdout, "{:02x} ", b)?; + let byte_str = format!("{:02x} ", b); + write!(self.stdout, "{}", Byte(b).color().paint(byte_str))?; self.line.push(b); match self.idx % 16 { @@ -56,9 +117,10 @@ impl<'a> Printer<'a> { write!(self.stdout, "{} │", " ".repeat(fill_spaces))?; - for b in self.line.iter().cloned() { - let chr = if byte_is_printable(b) { b as char } else { '.' }; - write!(self.stdout, "{}", chr).ok(); + for b in self.line.iter().map(|b| Byte(*b)) { + let chr = format!("{}", b.as_char()); + + write!(self.stdout, "{}", b.color().paint(chr)).ok(); } writeln!(self.stdout, "│");