#![allow(clippy::unwrap_used)]
use std::error::Error;
use std::fmt;
pub const MAX_LEN: usize = 11;
#[inline]
#[must_use]
pub fn encode_array(id: u64) -> ([u8; MAX_LEN], usize) {
let mut buf = [0u8; MAX_LEN];
let len = base62::encode_bytes(id, &mut buf).unwrap();
(buf, len)
}
#[inline]
pub fn encode_into(id: u64, buf: &mut [u8; MAX_LEN]) -> &str {
let len = base62::encode_bytes(id, buf).unwrap();
std::str::from_utf8(&buf[..len]).unwrap()
}
#[inline]
#[must_use]
pub fn encode(id: u64) -> String {
let (buf, len) = encode_array(id);
std::str::from_utf8(&buf[..len]).unwrap().to_owned()
}
pub fn decode(encoded: &str) -> Result<u64, DecodeError> {
let decoded = base62::decode(encoded).map_err(DecodeError::from)?;
let Ok(decoded_value) = u64::try_from(decoded) else {
return Err(DecodeError::Overflow);
};
Ok(decoded_value)
}
#[derive(Debug)]
pub enum DecodeError {
InvalidCharacter,
Overflow,
Other(base62::DecodeError),
}
impl fmt::Display for DecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Self::InvalidCharacter => write!(f, "Invalid base62 character"),
Self::Overflow => write!(f, "Decoded value would overflow u64"),
Self::Other(ref e) => write!(f, "Base62 decode error: {e}"),
}
}
}
impl Error for DecodeError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match *self {
Self::Other(ref e) => Some(e),
Self::InvalidCharacter | Self::Overflow => None,
}
}
}
impl From<base62::DecodeError> for DecodeError {
fn from(err: base62::DecodeError) -> Self {
Self::Other(err)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encode_decode_roundtrip() {
let values = [1u64, 1000, u64::MAX / 2, u64::MAX];
for &value in &values {
let encoded = encode(value);
let decoded = decode(&encoded).unwrap();
assert_eq!(value, decoded);
}
}
#[test]
fn test_encode_array_matches_string() {
let id = 12345678901234u64;
let string_encoded = encode(id);
let (arr, len) = encode_array(id);
let array_str = std::str::from_utf8(&arr[..len]).unwrap();
assert_eq!(string_encoded, array_str);
}
#[test]
fn test_encode_into_matches_string() {
let id = 98765432109876u64;
let string_encoded = encode(id);
let mut buf = [0u8; MAX_LEN];
let into_str = encode_into(id, &mut buf);
assert_eq!(string_encoded, into_str);
}
#[test]
fn test_max_len() {
let (_, len) = encode_array(u64::MAX);
assert!(len <= MAX_LEN);
}
#[test]
fn test_decode_error() {
assert!(decode("!!!").is_err());
}
}