use std::path::{Path, PathBuf};
#[must_use]
pub fn hex_encode(bytes: &[u8]) -> String {
bytes
.iter()
.fold(String::with_capacity(bytes.len() * 2), |mut output, b| {
use std::fmt::Write;
let _ = write!(output, "{b:02x}");
output
})
}
pub fn resolve_relative(path: &Path, project_root: &Path) -> std::io::Result<PathBuf> {
if path.is_absolute() {
Ok(path.to_path_buf())
} else {
project_root.join(path).canonicalize()
}
}
#[cfg(test)]
#[expect(clippy::unwrap_used, reason = "tests may use unwrap for brevity")]
mod tests {
use super::{hex_encode, resolve_relative};
use std::path::Path;
#[test]
fn hex_encode_empty() {
assert_eq!(hex_encode(&[]), "");
}
#[test]
fn hex_encode_single_bytes() {
assert_eq!(hex_encode(&[0x00]), "00");
assert_eq!(hex_encode(&[0xff]), "ff");
assert_eq!(hex_encode(&[0xab, 0xcd, 0xef]), "abcdef");
}
#[test]
fn resolve_relative_passes_absolute_through() {
let abs = Path::new("/does/not/exist/absolute");
let resolved = resolve_relative(abs, Path::new("/tmp")).unwrap();
assert_eq!(resolved, abs);
}
#[test]
fn resolve_relative_joins_relative_and_canonicalizes() {
let cwd = std::env::current_dir().unwrap();
let resolved = resolve_relative(Path::new("."), &cwd).unwrap();
assert_eq!(resolved, cwd.canonicalize().unwrap());
}
}