umya_spreadsheet/helper/utils.rs
1#![allow(unused_imports)]
2
3use md5::Digest as _;
4
5pub(crate) fn md5_hash(input: impl AsRef<[u8]>) -> String {
6 const HEX: &[u8; 16] = b"0123456789abcdef";
7
8 let digest = md5::Md5::digest(input);
9 let mut output = String::with_capacity(digest.len() * 2);
10 for byte in digest.iter().copied() {
11 output.push(HEX[(byte >> 4) as usize] as char);
12 output.push(HEX[(byte & 0x0f) as usize] as char);
13 }
14 output
15}
16
17pub(crate) fn unescape_xml_text(e: &quick_xml::events::BytesText<'_>) -> String {
18 let decoded = e.decode().unwrap();
19 quick_xml::escape::unescape(decoded.as_ref())
20 .unwrap()
21 .into_owned()
22}
23
24/// A macro that implements the `From` trait for converting from one error type
25/// to another.
26///
27/// # Usage
28/// ```
29/// # use std::{io,fmt};
30///
31/// use umya_spreadsheet::from_err;
32///
33/// #[derive(Debug)]
34/// enum MyError {
35/// Io(std::io::Error),
36/// };
37/// # impl fmt::Display for MyError {
38/// # fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39/// # unimplemented!()
40/// # }
41/// # }
42/// # impl std::error::Error for MyError {}
43///
44/// from_err!(std::io::Error, MyError, Io);
45///
46/// let io_err = io::Error::new(io::ErrorKind::Other, "An I/O error occurred");
47/// let my_err: MyError = io_err.into();
48/// ```
49#[macro_export]
50macro_rules! from_err {
51 ($from:ty, $to:tt, $var:tt) => {
52 impl From<$from> for $to {
53 #[inline]
54 fn from(e: $from) -> $to {
55 $to::$var(e)
56 }
57 }
58 };
59}
60
61/// Asserts that the SHA-256 hash of a given input matches the expected
62/// hexadecimal string.
63///
64/// # Arguments
65///
66/// * `$input` - The input data to hash.
67/// * `$expected_hex` - The expected SHA-256 hash as a hexadecimal string.
68///
69/// # Panics
70///
71/// This macro will panic if the actual SHA-256 hash does not match the
72/// expected hash.
73///
74/// # Examples
75///
76/// ```ignore
77/// let data = b"Hello, world!";
78/// assert_sha256!(
79/// data,
80/// "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e"
81/// );
82/// // This will not panic
83///
84/// assert_sha256!(data, "invalid_hash");
85/// // This will panic with a message indicating the mismatch
86/// ```
87macro_rules! assert_sha256 {
88 ($input:expr, $expected_hex:expr) => {{
89 let hash = Sha256::digest($input).to_vec();
90 let expected_bytes = hex_literal::hex!($expected_hex);
91 assert_eq!(
92 &hash,
93 &expected_bytes,
94 "SHA256({}) mismatch! Expected: {:?}, Actual: {:?}",
95 stringify!($input),
96 &expected_bytes
97 .iter()
98 .map(|b| format!("{:02x}", b))
99 .collect::<String>(),
100 &hash
101 .iter()
102 .map(|b| format!("{:02x}", b))
103 .collect::<String>()
104 );
105 }};
106}
107
108/// A macro that compiles a regular expression and caches it.
109///
110/// # Usage
111/// ```rust,ignore
112/// // Unable to run because function is private
113/// let re = compile_regex!(r"^\d+$");
114///
115/// assert!(re.is_match("123").unwrap());
116/// assert!(!re.is_match("abc").unwrap());
117/// ```
118macro_rules! compile_regex {
119 ($re:literal $(,)?) => {{
120 static RE: std::sync::OnceLock<fancy_regex::Regex> = std::sync::OnceLock::new();
121 RE.get_or_init(|| fancy_regex::Regex::new($re).unwrap())
122 }};
123}
124
125/// Prints a byte slice as a hex string, prefixed with the variable name.
126///
127/// This macro takes a reference to a byte slice (`&[u8]`) and prints both
128/// the variable name and its hexadecimal representation to stdout.
129macro_rules! print_hex {
130 ($var:expr) => {
131 println!(
132 "{} = {}",
133 stringify!($var),
134 $var.iter()
135 .map(|b| format!("{:02x}", b))
136 .collect::<String>()
137 );
138 };
139}
140
141/// Prints the SHA-256 hash of a given input as a hexadecimal string.
142///
143/// # Examples
144///
145/// ```ignore
146/// let data = b"Hello, world!";
147/// print_sha256_hex!(data);
148/// // Output: SHA256(data) = 5eb63bbbe01eeed093cb22bb8f5acdc3
149/// ```
150macro_rules! print_sha256_hex {
151 ($var:expr) => {
152 let hash = Sha256::digest($var);
153 println!(
154 "SHA256({}) = {}",
155 stringify!($var),
156 hash.iter()
157 .map(|b| format!("{:02x}", b))
158 .collect::<String>()
159 );
160 };
161}
162
163pub(crate) use assert_sha256;
164pub(crate) use compile_regex;
165pub(crate) use print_hex;
166pub(crate) use print_sha256_hex;