diff options
author | Julian Sitkevich <1553398+sitkevij@users.noreply.github.com> | 2020-10-29 07:55:07 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-10-29 07:55:07 -0700 |
commit | de4c13e8c3703773a746cff9df7cd02b3e8c7f8b (patch) | |
tree | 22d4cdace6b8eb2e949815d9b10325860cdf8aa3 | |
parent | e62a3a6f70e1fa66848aa82611b53415b934104b (diff) | |
parent | 3e508eab230297a5b938ccbce7d2cdc5f3b43892 (diff) |
Merge pull request #29 from sitkevij/developv0.3.1
develop 0.3.1
-rw-r--r-- | Cargo.lock | 69 | ||||
-rw-r--r-- | Cargo.toml | 5 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | src/lib.rs | 178 | ||||
-rw-r--r-- | src/main.rs | 6 | ||||
-rwxr-xr-x | tests.sh | 14 |
6 files changed, 215 insertions, 59 deletions
@@ -9,6 +9,18 @@ dependencies = [ ] [[package]] +name = "assert_cmd" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "doc-comment 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "predicates 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "predicates-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "predicates-tree 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wait-timeout 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "atty" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -38,10 +50,21 @@ dependencies = [ ] [[package]] +name = "difference" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "hx" -version = "0.3.0" +version = "0.3.1" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "assert_cmd 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -51,6 +74,29 @@ version = "0.2.47" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "predicates" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "predicates-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "predicates-core" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "predicates-tree" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "predicates-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "treeline 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "redox_syscall" version = "0.1.50" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -87,6 +133,11 @@ dependencies = [ ] [[package]] +name = "treeline" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "unicode-width" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -97,6 +148,14 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "winapi" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -117,17 +176,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum assert_cmd 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c88b9ca26f9c16ec830350d309397e74ee9abdfd8eb1f71cb6ecc71a3fc818da" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" +"checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" +"checksum doc-comment 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" "checksum libc 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)" = "48450664a984b25d5b479554c29cc04e3150c97aa4c01da5604a2d4ed9151476" +"checksum predicates 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96bfead12e90dccead362d62bb2c90a5f6fc4584963645bc7f71a735e0b0735a" +"checksum predicates-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "06075c3a3e92559ff8929e7a280684489ea27fe44805174c3ebd9328dcb37178" +"checksum predicates-tree 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8e63c4859013b38a76eca2414c64911fba30def9e3202ac461a2d22831220124" "checksum redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)" = "52ee9a534dc1301776eff45b4fa92d2c39b1d8c3d3357e6eb593e0d795506fc2" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" +"checksum treeline 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +"checksum wait-timeout 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" @@ -7,7 +7,7 @@ repository = "https://github.com/sitkevij/hex" keywords = ["hexdump", "hexadecimal", "tools", "ascii", "hex"] license = "MIT" name = "hx" -version = "0.3.0" +version = "0.3.1" edition = "2018" # see https://doc.rust-lang.org/cargo/reference/manifest.html @@ -30,3 +30,6 @@ opt-level = 3 [dependencies] clap = "2.31.1" ansi_term = "0.11" + +[dev-dependencies] +assert_cmd = "1.0.1" @@ -89,7 +89,7 @@ cargo install --path . Which will compile the release version, run tests and install release binary to `<USERDIR>/.cargo/bin/hx`. -If `<USERDIR>/.cargo/bin` is part of the `PATH` evironment variable, `hx` should be able to be executed anywhere in the shell. +If `<USERDIR>/.cargo/bin` is part of the `PATH` environment variable, `hx` should be able to be executed anywhere in the shell. ## features @@ -21,7 +21,7 @@ use std::error::Error; use std::f64; use std::fs; use std::io::BufReader; -use std::io::{self, BufRead, Read}; +use std::io::{self, BufRead, Read, Write}; /// arg cols pub const ARG_COL: &str = "cols"; @@ -134,8 +134,8 @@ pub fn offset(b: u64) -> String { } /// print offset to std out -pub fn print_offset(b: u64) { - print!("{}: ", offset(b)); +pub fn print_offset(w: &mut impl Write, b: u64) -> io::Result<()> { + write!(w, "{}: ", offset(b)) } /// hex octal, takes u8 @@ -159,7 +159,7 @@ pub fn hex_binary(b: u8) -> String { } /// print byte to std out -pub fn print_byte(b: u8, format: Format, colorize: bool) { +pub fn print_byte(w: &mut impl Write, b: u8, format: Format, colorize: bool) -> io::Result<()> { let mut color: u8 = b; if color < 1 { color = 0x16; @@ -167,39 +167,43 @@ pub fn print_byte(b: u8, format: Format, colorize: bool) { if colorize { // note, for color testing: for (( i = 0; i < 256; i++ )); do echo "$(tput setaf $i)This is ($i) $(tput sgr0)"; done match format { - Format::Octal => print!( + Format::Octal => write!( + w, "{} ", ansi_term::Style::new() .fg(ansi_term::Color::Fixed(color)) .paint(hex_octal(b)) ), - Format::LowerHex => print!( + Format::LowerHex => write!( + w, "{} ", ansi_term::Style::new() .fg(ansi_term::Color::Fixed(color)) .paint(hex_lower_hex(b)) ), - Format::UpperHex => print!( + Format::UpperHex => write!( + w, "{} ", ansi_term::Style::new() .fg(ansi_term::Color::Fixed(color)) .paint(hex_upper_hex(b)) ), - Format::Binary => print!( + Format::Binary => write!( + w, "{} ", ansi_term::Style::new() .fg(ansi_term::Color::Fixed(color)) .paint(hex_binary(b)) ), - _ => print!("unk_fmt "), + _ => write!(w, "unk_fmt "), } } else { match format { - Format::Octal => print!("{} ", hex_octal(b)), - Format::LowerHex => print!("{} ", hex_lower_hex(b)), - Format::UpperHex => print!("{} ", hex_upper_hex(b)), - Format::Binary => print!("{} ", hex_binary(b)), - _ => print!("unk_fmt "), + Format::Octal => write!(w, "{} ", hex_octal(b)), + Format::LowerHex => write!(w, "{} ", hex_lower_hex(b)), + Format::UpperHex => write!(w, "{} ", hex_upper_hex(b)), + Format::Binary => write!(w, "{} ", hex_binary(b)), + _ => write!(w, "unk_fmt "), } } } @@ -234,9 +238,9 @@ pub fn run(matches: ArgMatches) -> Result<(), Box<dyn Error>> { let mut buf: Box<dyn BufRead> = if is_stdin.unwrap() { Box::new(BufReader::new(io::stdin())) } else { - Box::new(BufReader::new( - fs::File::open(matches.value_of(ARG_INP).unwrap()).unwrap(), - )) + Box::new(BufReader::new(fs::File::open( + matches.value_of(ARG_INP).unwrap(), + )?)) }; let mut format_out = Format::LowerHex; let mut colorize = true; @@ -246,7 +250,7 @@ pub fn run(matches: ArgMatches) -> Result<(), Box<dyn Error>> { } if let Some(length) = matches.value_of(ARG_LEN) { - truncate_len = length.parse::<u64>().unwrap(); + truncate_len = length.parse::<u64>()?; } if let Some(format) = matches.value_of(ARG_FMT) { @@ -274,7 +278,7 @@ pub fn run(matches: ArgMatches) -> Result<(), Box<dyn Error>> { // array output mode is mutually exclusive if let Some(array) = matches.value_of(ARG_ARR) { - output_array(array, buf, truncate_len, column_width); + output_array(array, buf, truncate_len, column_width)?; } else { // Transforms this Read instance to an Iterator over its bytes. // The returned type implements Iterator where the Item is @@ -285,15 +289,18 @@ pub fn run(matches: ArgMatches) -> Result<(), Box<dyn Error>> { 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).unwrap(); + 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(offset_counter); + print_offset(&mut locked, offset_counter)?; for hex in line.hex_body.iter() { offset_counter += 1; byte_column += 1; - print_byte(*hex, format_out, colorize); + print_byte(&mut locked, *hex, format_out, colorize)?; if *hex > 31 && *hex < 127 { ascii_line.ascii.push(*hex as char); @@ -303,17 +310,21 @@ pub fn run(matches: ArgMatches) -> Result<(), Box<dyn Error>> { } if byte_column < column_width { - print!("{:<1$}", "", 5 * (column_width - byte_column) as usize); + write!( + locked, + "{:<1$}", + "", + 5 * (column_width - byte_column) as usize + )?; } byte_column = 0x0; let ascii_string: String = ascii_line.ascii.iter().cloned().collect(); ascii_line = Line::new(); - print!("{}", ascii_string); // print ascii string - println!(); + writeln!(locked, "{}", ascii_string)?; // print ascii string } if true { - println!(" bytes: {}", page.bytes); + writeln!(locked, " bytes: {}", page.bytes)?; } } } @@ -330,24 +341,18 @@ pub fn is_stdin(matches: ArgMatches) -> Result<bool, Box<dyn Error>> { dbg!(env::args().len(), matches.args.len()); dbg!(env::args().nth(0).unwrap()); } - if let Some(nth1) = env::args().nth(1) { + if let Some(file) = matches.value_of(ARG_INP) { if DBG > 0 { - dbg!(nth1); + dbg!(file); } - for arg in ARGS.iter() { - if let Some(index) = matches.index_of(arg) { - if let 2 = index { - is_stdin = true; - } - } + is_stdin = false; + } else if let Some(nth1) = env::args().nth(1) { + if DBG > 0 { + dbg!(nth1); } + is_stdin = ARGS.iter().any(|arg| matches.index_of(arg) == Some(2)); } else if matches.args.is_empty() { is_stdin = true; - } else if let Some(file) = matches.value_of(ARG_INP) { - if DBG > 0 { - dbg!(file); - } - is_stdin = false; } if DBG > 0 { dbg!(is_stdin); @@ -367,33 +372,41 @@ pub fn output_array( mut buf: Box<dyn BufRead>, truncate_len: u64, column_width: u64, -) { +) -> io::Result<()> { + let stdout = io::stdout(); + let mut locked = stdout.lock(); + let page = buf_to_array(&mut buf, truncate_len, column_width).unwrap(); match array_format { - "r" => println!("let ARRAY: [u8; {}] = [", page.bytes), - "c" => println!("unsigned char ARRAY[{}] = {{", page.bytes), - "g" => println!("a := [{}]byte{{", page.bytes), - _ => println!("unknown array format"), + "r" => writeln!(locked, "let ARRAY: [u8; {}] = [", page.bytes)?, + "c" => writeln!(locked, "unsigned char ARRAY[{}] = {{", page.bytes)?, + "g" => writeln!(locked, "a := [{}]byte{{", page.bytes)?, + _ => writeln!(locked, "unknown array format")?, } let mut i: u64 = 0x0; for line in page.body.iter() { - print!(" "); + write!(locked, " ")?; for hex in line.hex_body.iter() { i += 1; if i == page.bytes && array_format != "g" { - print!("{}", hex_lower_hex(*hex)); + write!(locked, "{}", hex_lower_hex(*hex))?; } else { - print!("{}, ", hex_lower_hex(*hex)); + write!(locked, "{}, ", hex_lower_hex(*hex))?; } } - println!(); - } - match array_format { - "r" => println!("];"), - "c" => println!("}};"), - "g" => println!("}}"), - _ => println!("unknown array format"), + writeln!(locked)?; } + + writeln!( + locked, + "{}", + match array_format { + "r" => "];", + "c" => "};", + "g" => "}", + _ => "unknown array format", + } + ) } /// Function wave out. @@ -433,7 +446,7 @@ pub fn buf_to_array( let mut page: Page = Page::new(); let mut line: Line = Line::new(); for b in buf.bytes() { - let b1: u8 = b.unwrap(); + let b1: u8 = b?; line.bytes += 1; page.bytes += 1; line.hex_body.push(b1); @@ -495,4 +508,59 @@ mod tests { assert_eq!(hex_binary(b), "0b11111111"); assert_eq!(hex_binary(b), format!("{:#010b}", b)); } + use assert_cmd::Command; + + /// target/debug/hx -ar tests/files/tiny.txt + #[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) + .stdout("let ARRAY: [u8; 3] = [\n 0x69, 0x6c, 0x0a\n];\n"); + } + + /// target/debug/hx tests/files/tiny.txt -ar + #[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) + .stdout("let ARRAY: [u8; 3] = [\n 0x69, 0x6c, 0x0a\n];\n"); + } + + /// target/debug/hx --len tests/files/tiny.txt + /// error: invalid digit found in string + #[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", + ); + } } diff --git a/src/main.rs b/src/main.rs index d008aba..2c19a30 100644 --- a/src/main.rs +++ b/src/main.rs @@ -85,7 +85,11 @@ fn main() { process::exit(0); } Err(e) => { - eprintln!("error = \"{}\"", e); + eprintln!( + "{} {}", + ansi_term::Colour::Fixed(9).bold().paint("error:"), + e + ); process::exit(1); } } diff --git a/tests.sh b/tests.sh new file mode 100755 index 0000000..b6782cd --- /dev/null +++ b/tests.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +# param ordering 1 +target/debug/hx -ar tests/files/tiny.txt +# param ordering 2 +target/debug/hx tests/files/tiny.txt -ar +# binary output column width 4 +target/debug/hx -c4 -fb tests/files/alphanumeric.txt +# missing len param +target/debug/hx --len tests/files/tiny.txt +# missing file name +target/debug/hx missing-file +# simulate broken pipe +dd if=/dev/random bs=512 count=10 | RUST_BACKTRACE=1 target/debug/hx | head -n 10 |