use super::{TAG, identifier};
use crate::serve::protocol::Version;
use crate::serve::storage::Failure;
const VERSION_BYTES: usize = 16;
#[must_use]
pub fn version_prefix(version: Version) -> Vec<u8> {
let mut prefix = Vec::with_capacity(TAG.len() + VERSION_BYTES + 1);
prefix.extend_from_slice(TAG);
prefix.extend_from_slice(&version.generation.to_be_bytes());
prefix.extend_from_slice(&version.sequence.to_be_bytes());
prefix.push(0);
prefix
}
pub fn key(version: Version, request_id: &str) -> Result<Vec<u8>, Failure> {
identifier(request_id)?;
let mut key = version_prefix(version);
key.extend_from_slice(request_id.as_bytes());
Ok(key)
}
pub fn decode_key(key: &[u8]) -> Result<(Version, &str), Failure> {
let invalid = || {
Failure::new(
"change_record_key_invalid",
"attribution key has invalid tag, version or request id",
)
};
let suffix = key.strip_prefix(TAG).ok_or_else(invalid)?;
let generation = suffix
.get(..8)
.ok_or_else(invalid)?
.try_into()
.map_err(|_| invalid())?;
let sequence = suffix
.get(8..16)
.ok_or_else(invalid)?
.try_into()
.map_err(|_| invalid())?;
if suffix.get(VERSION_BYTES) != Some(&0) {
return Err(invalid());
}
let request = std::str::from_utf8(suffix.get(VERSION_BYTES + 1..).ok_or_else(invalid)?)
.map_err(|_| invalid())?;
identifier(request).map_err(|_| invalid())?;
Ok((
Version {
generation: u64::from_be_bytes(generation),
sequence: u64::from_be_bytes(sequence),
},
request,
))
}