use std::{iter::once, str::from_utf8};
use hitbox_core::{CacheKey, KeyPart};
use crate::format::FormatError;
const PREFIX_KEY: &str = "_prefix";
const VERSION_KEY: &str = "_version";
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
pub enum CacheKeyFormat {
#[default]
Bitcode,
UrlEncoded,
}
impl CacheKeyFormat {
pub fn serialize(&self, key: &CacheKey) -> Result<Vec<u8>, FormatError> {
match self {
CacheKeyFormat::Bitcode => Ok(bitcode::encode(key)),
CacheKeyFormat::UrlEncoded => {
let pairs = once((PREFIX_KEY, key.prefix().to_string()))
.chain(once((VERSION_KEY, key.version().to_string())))
.chain(
key.parts()
.map(|p| (p.key(), p.value().unwrap_or_default().to_string())),
)
.collect::<Vec<_>>();
serde_urlencoded::to_string(pairs)
.map(String::into_bytes)
.map_err(|err| FormatError::Serialize(Box::new(err)))
}
}
}
pub fn deserialize(&self, data: &[u8]) -> Result<CacheKey, FormatError> {
match self {
CacheKeyFormat::Bitcode => {
bitcode::decode(data).map_err(|err| FormatError::Deserialize(Box::new(err)))
}
CacheKeyFormat::UrlEncoded => {
let input =
from_utf8(data).map_err(|err| FormatError::Deserialize(Box::new(err)))?;
let pairs: Vec<(String, String)> = serde_urlencoded::from_str(input)
.map_err(|err| FormatError::Deserialize(Box::new(err)))?;
let (mut prefix, mut version, mut parts) = (String::new(), 0u32, Vec::new());
for (key, value) in pairs {
match key.as_str() {
PREFIX_KEY => prefix = value,
VERSION_KEY => {
version = value
.parse()
.map_err(|err| FormatError::Deserialize(Box::new(err)))?;
}
_ => {
let v = if value.is_empty() { None } else { Some(value) };
parts.push(KeyPart::new(key, v));
}
}
}
Ok(CacheKey::new(prefix, version, parts))
}
}
}
}