hextool/
lib.rs

1//! This crate provides functions that allows you to easily convert strings from and to hex.
2//!
3//!
4//! ## Usage
5//!
6//! This crate is [on crates.io](https://crates.io/crates/hextool) and can be added to your project.
7//! ```toml
8//! [dependencies]
9//! hextool = { version = "version", default-features = false }
10//! ```
11//! ## Examples
12//!
13//! ### Example: converting string to hex
14//!
15//! To convert a string to hex, use the `Hex` and `Convert` trait:
16//!
17//! ```
18//! use hextool::{Hex, Convert};
19//!
20//! // Convert a string to hex
21//! let hex = Hex::convert("hello", false, false);
22//! println!("hello in hex: {}", hex); // #=> "hello in hex: 68656c6c6f"
23//!
24//! // You can also split the output in bytes
25//! let hex = Hex::convert("hello", false, true);
26//! println!("hello in hex: {}", hex); // #=> "hello in hex: 68 65 6c 6c 6f"
27//!
28//! // Convert a string with numeric flag. This will take the numerical value of the string.
29//! // If the string is not a number, it will return an error.
30//! let hex = Hex::convert("255", true, false);
31//! println!("255 in hex: {}", hex); // #=> "255 in hex: {ff}"
32//! ```
33//! ### Example: converting hex to string
34//!
35//! To convert a hex string to a string, use the `UnHex` and `Convert` trait:
36//!
37//! ```
38//! use hextool::{UnHex, Convert};
39//!
40//! // Convert a hex string to a string
41//! let unhex = UnHex::convert("68656c6c6f", false, false);
42//! println!("68656c6c6f in string: {}", unhex); // #=> "68656c6c6f in string: hello"
43//!
44//! // Convert a hex string to a string with numeric flag. This will take the numerical value of the string.
45//! let unhex = UnHex::convert("cafebabe", true, false);
46//! println!("cafebabe in string: {}", unhex); // #=> "cafebabe in string: 3405691582"
47//!
48//! // If numeric is set to false, only valid strings [0-9a-f] is accepted. If the string is not valid,
49//! // it will return the string with the invalid string highlighted to red.
50//! let unhex = UnHex::convert("aga", true, false);
51//! println!("{}", unhex); // #=> "The highlighted chars can't be converted:\nag\u{1b}[31ma\u{1b}[0m."
52//!```
53
54#![doc(html_root_url = "https://docs.rs/hextool/0.1.1")]
55#![deny(missing_docs)]
56
57use std::error::Error;
58use std::fmt::{Debug, Display, Formatter};
59use regex::Regex;
60
61/// Error type for hextool
62pub struct HexToolError {
63    /// Error message
64    pub(crate) message: String,
65}
66
67/// Implement Debug, Display and Error for HexToolError
68impl Debug for HexToolError {
69    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
70        write!(f, "{}", self.message)
71    }
72}
73
74/// Implement Debug, Display and Error for HexToolError
75impl Display for HexToolError {
76    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
77        write!(f, "{}", self.message)
78    }
79}
80
81/// Implement Debug, Display and Error for HexToolError
82impl Error for HexToolError {}
83
84
85/// Convert trait
86pub trait Convert {
87    /// Convert input to hex or string depending on the implementation
88    ///
89    /// ## Arguments
90    /// * `input` - The input to convert
91    /// * `numeric` - If set to true, the input will be considered as a numeric value.
92    /// * `split` - If set to true, the output will be split in bytes
93    /// ## Returns
94    /// A string with the converted input
95    fn convert(input: &str, numeric: bool, split: bool) -> String;
96}
97
98/// Hex struct
99///
100/// This struct implements the Convert trait and can be used to convert a string to hex.
101///
102/// ## Example
103/// ```
104/// use hextool::{Hex, Convert};
105///
106/// // Convert a string to hex
107/// let hex = Hex::convert("hello", false, false);
108/// println!("hello in hex: {}", hex); // #=> "hello in hex: 68656c6c6f"
109/// ```
110pub struct Hex;
111
112impl Hex {
113    fn hex_string(input: &str) -> Result<String, HexToolError> {
114        return Ok(input.as_bytes().iter().map(|b| format!("{:02x}", b)).collect());
115    }
116
117    fn hex_numeric(input: &str) -> Result<String, HexToolError> {
118        let is_all_digit = input.chars().all(|c| c.is_digit(10));
119        if !is_all_digit {
120            return Err(HexToolError {
121                message: String::from("Input is not valid for 'hex' with numeric flag (-n).")
122            });
123        }
124        let to_str: i64 = input.parse().unwrap();
125        Ok(format!("{:02x}", to_str))
126    }
127}
128
129impl Convert for Hex {
130    fn convert(input: &str, numeric: bool, split_byte: bool) -> String {
131        let result;
132        if numeric {
133            result = Hex::hex_numeric(input);
134        } else {
135            result = Hex::hex_string(input);
136        }
137
138        match result {
139            Ok(str) => {
140                if split_byte {
141                    let mut result = String::new();
142                    for (i, c) in str.chars().enumerate() {
143                        if i % 2 == 0 && i != 0 {
144                            result.push_str(" ");
145                        }
146                        result.push(c);
147                    }
148                    format!("{}", result)
149                } else {
150                    format!("{}", str)
151                }
152            }
153            Err(e) => e.to_string(),
154        }
155    }
156}
157
158/// UnHex struct
159///
160/// This struct implements the Convert trait and can be used to convert a hex string to a string.
161///
162/// ## Example
163/// ```
164/// use hextool::{UnHex, Convert};
165///
166/// // Convert a hex string to a string
167/// let unhex = UnHex::convert("68656c6c6f", false, false);
168/// println!("68656c6c6f in string: {}", unhex); // #=> "68656c6c6f in string: hello"
169/// ```
170pub struct UnHex;
171
172impl UnHex {
173    fn un_hex_numeric(input: &str) -> Result<String, HexToolError> {
174        let parsed = i64::from_str_radix(input, 16);
175        match parsed {
176            Ok(i) => Ok(format!("{}", i)),
177            Err(e) => Err(HexToolError {
178                message: format!("Input is not valid for 'unhex' with numeric flag (-n). {}", e)
179            })
180        }
181    }
182
183    fn un_hex_string(input: &str) -> Result<String, HexToolError> {
184        // Go through each hex byte and convert to a string
185        let mut result = String::new();
186        let mut i = 0;
187
188        while i < input.len() {
189            let byte = &input[i..i + 2];
190            let parsed = u8::from_str_radix(byte, 16);
191            match parsed {
192                Ok(i) => result.push(i as char),
193                Err(e) => return Err(HexToolError {
194                    message: format!("Could not parse {}", e)
195                })
196            }
197            i += 2;
198        }
199        Ok(result)
200    }
201
202    fn validate_hex(input: &str) -> Result<String, HexToolError> {
203        let re = Regex::new("0[x|X]").unwrap();
204        let cleaned = re.replace_all(input, "");
205
206        let mut is_valid = true;
207        let mut proc_input = String::new();
208        for c in cleaned.chars() {
209            if !c.is_digit(16) {
210                if is_valid { is_valid = false };
211                proc_input.push_str(&format!("\x1b[31m{}\x1b[0m", c));
212                continue;
213            }
214            proc_input.push(c);
215        }
216        if !is_valid {
217            return Err(HexToolError {
218                message: format!("The highlighted chars can't be converted:\n{}", proc_input)
219            });
220        }
221
222        if proc_input.len() % 2 != 0 {
223            proc_input = format!("0{}", proc_input)
224        };
225        Ok(proc_input.to_string())
226    }
227}
228
229impl Convert for UnHex {
230    fn convert(input: &str, numeric: bool, split_byte: bool) -> String {
231        let valid_input;
232        match UnHex::validate_hex(input) {
233            Ok(str) => { valid_input = str; }
234            Err(e) => return e.to_string()
235        }
236
237        let result;
238        if numeric {
239            result = UnHex::un_hex_numeric(&valid_input);
240        } else {
241            result = UnHex::un_hex_string(&valid_input);
242        }
243
244        match result {
245            Ok(str) => {
246                if !split_byte { return format!("{}", str); }
247                return str.chars().map(|c| format!("{}", c))
248                    .collect::<Vec<String>>().join(" ");
249            }
250            Err(e) => { e.to_string() }
251        }
252    }
253}