trick_data/services/convert/
to_hex.rs

1// Might be useful later: https://stackoverflow.com/a/25265605
2
3use std::str::Bytes;
4
5pub trait ToHex {
6    fn to_hex(&self) -> String;
7}
8
9impl ToHex for str {
10    /// Converts the input string to hexadecimal bytes representation in string
11    /// separated by the specified delimiter.
12    fn to_hex(&self) -> String {
13        if self.is_empty() {
14            return String::new();
15        }
16
17        let mut result = allocate_string(self.bytes().len());
18        result.fill_from(&mut self.bytes());
19
20        result
21    }
22}
23
24fn allocate_string(input_data_length: usize) -> String {
25    let mut result = String::new();
26
27    // Each hexadecimal value takes two bytes.
28    let hexes_size = input_data_length * 2;
29    // Last space from string is trimmed.
30    let spaces_size = input_data_length - 1;
31
32    result.reserve(hexes_size + spaces_size);
33
34    result
35}
36
37trait Fill {
38    fn fill_from(&mut self, byte_iter: &mut Bytes<'_>);
39}
40
41impl Fill for String {
42    fn fill_from(&mut self, byte_iter: &mut Bytes<'_>) {
43        for _ in 0..(byte_iter.len() - 1) {
44            self.push_str(&format!("{:0>2x} ", byte_iter.next().unwrap()));
45        }
46        self.push_str(&format!("{:0>2x}", byte_iter.next().unwrap()));
47    }
48}
49
50#[cfg(test)]
51mod test {
52    use super::*;
53
54    /// Common way to test positive test cases
55    macro_rules! positive_tests {
56        ($($name:ident: $value:expr,)*) => {
57            $(
58                #[test]
59                fn $name() {
60                    // Aggregate
61                    let (input_data, expected) = $value;
62
63                    // Act
64                    let actual = input_data.to_hex();
65
66                    // Assert
67                    assert_eq!(actual, expected)
68                }
69            )*
70        }
71    }
72
73    positive_tests! {
74        empty_string: (&String::new(), ""),
75        digits: ("0123456789", "30 31 32 33 34 35 36 37 38 39"),
76        lowercase_ascii: (
77            "abcdefghijklmnopqrstuvwxyz",
78            "61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a"),
79        uppercase_ascii: (
80            "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
81            "41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a"),
82        utf8_1byte_first_code_point: ("\u{0000}", "00"),
83        utf8_1byte_last_code_point: ("\u{007F}", "7f"),
84        utf8_2bytes_first_code_point: ("\u{0080}", "c2 80"),
85        utf8_2bytes_last_code_point: ("\u{07FF}", "df bf"),
86        utf8_3bytes_first_code_point: ("\u{0800}", "e0 a0 80"),
87        utf8_3bytes_last_code_point: ("\u{FFFF}", "ef bf bf"),
88        utf8_4bytes_first_code_point: ("\u{10000}", "f0 90 80 80"),
89        utf8_4bytes_last_code_point: ("\u{10FFFF}", "f4 8f bf bf"),
90    }
91
92    #[test]
93    fn returned_string_size() {
94        // Act
95        let response = "a".to_hex();
96        // Assert
97        assert!(response.len() <= response.capacity());
98    }
99}