use crate::salt::generate_salt;
use anyhow::Result;
use hash_ids::HashIds;
use log::debug;
pub fn decode(hash: impl AsRef<str>) -> Result<Vec<u64>> {
let hash = hash.as_ref();
let hash_ids = hashids();
let decode = hash_ids.decode(hash)?;
debug!("Decoding: {} -> {:?}", hash, decode);
Ok(decode)
}
pub fn encode(data: &[u64]) -> String {
let hash_ids = hashids();
let encode = hash_ids.encode(data);
debug!("Encoding: {:?} -> {}", data, encode);
encode
}
pub fn decode_single(hash: impl AsRef<str>) -> Result<u64> {
let hash = hash.as_ref(); let decode = decode(hash)?;
if decode.len() != 1 {
return Err(anyhow::Error::msg(format!("Invalid hash: {}", hash))); }
Ok(decode[0])
}
pub fn encode_single(data: u64) -> String {
encode(&[data]) }
fn hashids() -> HashIds {
let options = get_hash_options();
HashIds::builder()
.with_salt(options.salt.as_str())
.with_min_length(options.min_length)
.with_alphabet(options.alphabet.as_str())
.finish()
.unwrap()
}
use std::sync::OnceLock;
pub struct SerdeHashOptions {
pub salt: String,
pub min_length: usize,
pub alphabet: String,
}
impl Default for SerdeHashOptions {
fn default() -> Self {
Self {
salt: generate_salt(), min_length: 8, alphabet: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".to_string(),
}
}
}
static HASH_OPTIONS: OnceLock<SerdeHashOptions> = OnceLock::new();
pub fn get_hash_options() -> &'static SerdeHashOptions {
HASH_OPTIONS.get_or_init(SerdeHashOptions::default)
}
impl SerdeHashOptions {
pub fn new() -> Self {
Self::default()
}
pub fn with_salt(mut self, salt: impl AsRef<str>) -> Self {
self.salt = salt.as_ref().to_string(); self
}
pub fn with_min_length<T>(mut self, min_length: T) -> Self
where
T: TryInto<usize>,
<T as TryInto<usize>>::Error: std::fmt::Debug,
{
self.min_length = min_length.try_into().expect("Failed to convert to usize");
self
}
pub fn with_alphabet(mut self, alphabet: impl AsRef<str>) -> Self {
self.alphabet = alphabet.as_ref().to_string(); self
}
pub fn build(self) {
let _ = HASH_OPTIONS.set(self); }
}