#![deny(
dead_code,
missing_docs,
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unsafe_code,
unstable_features,
unused_import_braces,
unused_qualifications
)]
#![doc = include_str!("../README.md")]
extern crate ansi_term;
extern crate clap;
mod args;
mod array_output;
mod buffer;
mod format;
mod function_output;
mod models;
mod output;
pub use args::{
ARG_ARR, ARG_CLR, ARG_COL, ARG_FMT, ARG_FNC, ARG_INP, ARG_LEN, ARG_PFX, ARG_PLC, is_stdin,
};
pub use array_output::output_array;
pub use buffer::buf_to_array;
pub use format::{Format, FormatError};
pub use function_output::output_function;
pub use models::{Line, Page};
pub use output::{append_ascii, byte_to_color, offset, print_byte, print_offset};
use clap::ArgMatches;
use no_color::is_no_color;
use std::error::Error;
use std::fs;
use std::io::BufReader;
use std::io::IsTerminal;
use std::io::{self, BufRead, Write};
pub fn run(matches: ArgMatches) -> Result<(), Box<dyn Error>> {
let mut column_width: u64 = 10;
let mut truncate_len: u64 = 0x0;
if let Some(len) = matches.get_one::<String>("func") {
let mut p: usize = 4;
if let Some(places) = matches.get_one::<String>("places") {
p = match places.parse::<usize>() {
Ok(p) => p,
Err(e) => {
eprintln!("-p, --places <integer> expected. {:?}", e);
return Err(Box::new(e));
}
}
}
let length = len.parse::<u64>().map_err(|e| {
eprintln!("-u, --func <integer> expected. {:?}", e);
e
})?;
output_function(length, p);
} else {
let is_stdin = is_stdin(matches.clone())?;
let mut buf: Box<dyn BufRead> = if is_stdin {
Box::new(BufReader::new(io::stdin()))
} else {
let input_file = matches.get_one::<String>(ARG_INP).ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidInput, "Input file not specified")
})?;
Box::new(BufReader::new(fs::File::open(input_file)?))
};
let mut format_out = Format::LowerHex;
let mut colorize = true;
let mut prefix = true;
if let Some(columns) = matches.get_one::<String>(ARG_COL) {
column_width = match columns.parse::<u64>() {
Ok(column_width) => column_width,
Err(e) => {
eprintln!("-c, --cols <integer> expected. {:?}", e);
return Err(Box::new(e));
}
}
}
if let Some(length) = matches.get_one::<String>(ARG_LEN) {
truncate_len = match length.parse::<u64>() {
Ok(truncate_len) => truncate_len,
Err(e) => {
eprintln!("-l, --len <integer> expected. {:?}", e);
return Err(Box::new(e));
}
}
}
if let Some(format) = matches.get_one::<String>(ARG_FMT) {
match format.as_str() {
"o" => format_out = Format::Octal,
"x" => format_out = Format::LowerHex,
"X" => format_out = Format::UpperHex,
"p" => format_out = Format::Pointer,
"b" => format_out = Format::Binary,
"e" => format_out = Format::LowerExp,
"E" => format_out = Format::UpperExp,
_ => format_out = Format::Unknown,
}
}
if is_no_color() {
colorize = false;
}
if !io::stdout().is_terminal() {
colorize = false;
}
if let Some(color) = matches.get_one::<String>(ARG_CLR) {
colorize = color.parse::<u8>().map_err(|e| {
eprintln!("-t, --color <0|1> expected. {:?}", e);
e
})? == 1;
}
if let Some(prefix_flag) = matches.get_one::<String>(ARG_PFX) {
prefix = prefix_flag.parse::<u8>().map_err(|e| {
eprintln!("-r, --prefix <0|1> expected. {:?}", e);
e
})? == 1;
}
if let Some(array) = matches.get_one::<String>(ARG_ARR) {
output_array(array, buf, truncate_len, column_width)?;
} else {
let mut ascii_line: Line = Line::new();
let mut offset_counter: u64 = 0x0;
let mut byte_column: u64 = 0x0;
let page = buf_to_array(&mut buf, truncate_len, column_width)?;
let stdout = io::stdout();
let mut locked = stdout.lock();
for line in page.body.iter() {
print_offset(&mut locked, offset_counter)?;
for hex in line.hex_body.iter() {
offset_counter += 1;
byte_column += 1;
print_byte(&mut locked, *hex, format_out, colorize, prefix)?;
append_ascii(&mut ascii_line.ascii, *hex, colorize);
}
if byte_column < column_width {
write!(
locked,
"{:<1$}",
"",
5 * (column_width - byte_column) as usize
)?;
}
locked.write_all(ascii_line.ascii.as_slice())?;
writeln!(locked)?;
byte_column = 0x0;
ascii_line = Line::new();
}
if true {
writeln!(locked, " bytes: {}", page.bytes)?;
}
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use assert_cmd::Command;
#[test]
fn test_cli_arg_order_1() {
let mut cmd = Command::cargo_bin("hx").unwrap();
let assert = cmd.arg("-ar").arg("tests/files/tiny.txt").assert();
assert.success().code(0);
}
#[test]
fn test_cli_arg_order_2() {
let mut cmd = Command::cargo_bin("hx").unwrap();
let assert = cmd.arg("tests/files/tiny.txt").arg("-ar").assert();
assert.success().code(0);
}
#[test]
fn test_cli_missing_param_value() {
let mut cmd = Command::cargo_bin("hx").unwrap();
let assert = cmd.arg("--len").arg("tests/files/tiny.txt").assert();
assert.failure().code(1);
}
#[test]
fn test_cli_input_missing_file() {
let mut cmd = Command::cargo_bin("hx").unwrap();
let assert = cmd.arg("missing-file").assert();
assert.failure().code(1);
}
#[test]
fn test_cli_input_directory() {
let mut cmd = Command::cargo_bin("hx").unwrap();
let assert = cmd.arg("src").assert();
assert.failure().code(1);
}
#[test]
fn test_cli_input_stdin() {
let mut cmd = Command::cargo_bin("hx").unwrap();
let assert = cmd.arg("-t0").write_stdin("012").assert();
assert.success().code(0).stdout(
"0x000000: 0x30 0x31 0x32 012\n bytes: 3\n",
);
}
}