#![doc(html_root_url = "https://docs.rs/hextool/0.1.1")]
#![deny(missing_docs)]
use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use regex::Regex;
pub struct HexToolError {
pub(crate) message: String,
}
impl Debug for HexToolError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message)
}
}
impl Display for HexToolError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message)
}
}
impl Error for HexToolError {}
pub trait Convert {
fn convert(input: &str, numeric: bool, split: bool) -> String;
}
pub struct Hex;
impl Hex {
fn hex_string(input: &str) -> Result<String, HexToolError> {
return Ok(input.as_bytes().iter().map(|b| format!("{:02x}", b)).collect());
}
fn hex_numeric(input: &str) -> Result<String, HexToolError> {
let is_all_digit = input.chars().all(|c| c.is_digit(10));
if !is_all_digit {
return Err(HexToolError {
message: String::from("Input is not valid for 'hex' with numeric flag (-n).")
});
}
let to_str: i64 = input.parse().unwrap();
Ok(format!("{:02x}", to_str))
}
}
impl Convert for Hex {
fn convert(input: &str, numeric: bool, split_byte: bool) -> String {
let result;
if numeric {
result = Hex::hex_numeric(input);
} else {
result = Hex::hex_string(input);
}
match result {
Ok(str) => {
if split_byte {
let mut result = String::new();
for (i, c) in str.chars().enumerate() {
if i % 2 == 0 && i != 0 {
result.push_str(" ");
}
result.push(c);
}
format!("{}", result)
} else {
format!("{}", str)
}
}
Err(e) => e.to_string(),
}
}
}
pub struct UnHex;
impl UnHex {
fn un_hex_numeric(input: &str) -> Result<String, HexToolError> {
let parsed = i64::from_str_radix(input, 16);
match parsed {
Ok(i) => Ok(format!("{}", i)),
Err(e) => Err(HexToolError {
message: format!("Input is not valid for 'unhex' with numeric flag (-n). {}", e)
})
}
}
fn un_hex_string(input: &str) -> Result<String, HexToolError> {
let mut result = String::new();
let mut i = 0;
while i < input.len() {
let byte = &input[i..i + 2];
let parsed = u8::from_str_radix(byte, 16);
match parsed {
Ok(i) => result.push(i as char),
Err(e) => return Err(HexToolError {
message: format!("Could not parse {}", e)
})
}
i += 2;
}
Ok(result)
}
fn validate_hex(input: &str) -> Result<String, HexToolError> {
let re = Regex::new("0[x|X]").unwrap();
let cleaned = re.replace_all(input, "");
let mut is_valid = true;
let mut proc_input = String::new();
for c in cleaned.chars() {
if !c.is_digit(16) {
if is_valid { is_valid = false };
proc_input.push_str(&format!("\x1b[31m{}\x1b[0m", c));
continue;
}
proc_input.push(c);
}
if !is_valid {
return Err(HexToolError {
message: format!("The highlighted chars can't be converted:\n{}", proc_input)
});
}
if proc_input.len() % 2 != 0 {
proc_input = format!("0{}", proc_input)
};
Ok(proc_input.to_string())
}
}
impl Convert for UnHex {
fn convert(input: &str, numeric: bool, split_byte: bool) -> String {
let valid_input;
match UnHex::validate_hex(input) {
Ok(str) => { valid_input = str; }
Err(e) => return e.to_string()
}
let result;
if numeric {
result = UnHex::un_hex_numeric(&valid_input);
} else {
result = UnHex::un_hex_string(&valid_input);
}
match result {
Ok(str) => {
if !split_byte { return format!("{}", str); }
return str.chars().map(|c| format!("{}", c))
.collect::<Vec<String>>().join(" ");
}
Err(e) => { e.to_string() }
}
}
}