use data_encoding::{Encoding, Specification};
use once_cell::sync::Lazy;
use super::error::ElidError;
pub static BASE32HEX_LOWER_NOPAD: Lazy<Encoding> = Lazy::new(|| {
let mut spec = Specification::new();
spec.symbols.push_str("0123456789abcdefghijklmnopqrstuv");
spec.encoding().expect("Invalid base32hex specification")
});
pub fn encode_sortable(bytes: &[u8]) -> String {
BASE32HEX_LOWER_NOPAD.encode(bytes)
}
pub fn decode_sortable(s: &str) -> Result<Vec<u8>, ElidError> {
BASE32HEX_LOWER_NOPAD
.decode(s.as_bytes())
.map_err(|_| ElidError::InvalidEncoding)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_roundtrip() {
let original = vec![0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
let encoded = encode_sortable(&original);
let decoded = decode_sortable(&encoded).unwrap();
assert_eq!(original, decoded);
}
#[test]
fn test_sort_order_preservation() {
let bytes1 = vec![0x00, 0x00, 0x00, 0x01];
let bytes2 = vec![0x00, 0x00, 0x00, 0x02];
let bytes3 = vec![0x00, 0x00, 0x00, 0xFF];
let enc1 = encode_sortable(&bytes1);
let enc2 = encode_sortable(&bytes2);
let enc3 = encode_sortable(&bytes3);
assert!(enc1 < enc2);
assert!(enc2 < enc3);
assert!(enc1 < enc3);
}
#[test]
fn test_128_bit_length() {
let bytes = vec![0u8; 16];
let encoded = encode_sortable(&bytes);
assert_eq!(encoded.len(), 26);
}
#[test]
fn test_100_bit_length() {
let bytes = vec![0u8; 13];
let encoded = encode_sortable(&bytes);
assert_eq!(encoded.len(), 21);
}
#[test]
fn test_lowercase_alphabet() {
let bytes = vec![0xFF; 8];
let encoded = encode_sortable(&bytes);
assert!(encoded
.chars()
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit()));
}
#[test]
fn test_no_padding() {
let bytes = vec![0x01, 0x02, 0x03];
let encoded = encode_sortable(&bytes);
assert!(!encoded.contains('='));
}
#[test]
fn test_invalid_encoding() {
let result = decode_sortable("xyz123");
assert!(result.is_err());
match result {
Err(ElidError::InvalidEncoding) => {}
_ => panic!("Expected InvalidEncoding error"),
}
}
}