use std::time::{SystemTime, UNIX_EPOCH};
use super::types::{IncrError, KeyMatchMode, ValueMeta};
const VAL_META_PLAIN: u8 = 0x00;
const VAL_META_TTL: u8 = 0x01;
pub fn now_unix_secs() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0)
}
pub fn pack_plain(user: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(1 + user.len());
out.push(VAL_META_PLAIN);
out.extend_from_slice(user);
out
}
pub fn pack_ttl(user: &[u8], expire_unix_secs: u64) -> Vec<u8> {
let mut out = Vec::with_capacity(1 + 8 + user.len());
out.push(VAL_META_TTL);
out.extend_from_slice(&expire_unix_secs.to_le_bytes());
out.extend_from_slice(user);
out
}
pub fn decode_value_meta(logical: &[u8]) -> ValueMeta {
if logical.is_empty() {
return ValueMeta::Plain { user: Vec::new() };
}
match logical[0] {
VAL_META_PLAIN => ValueMeta::Plain {
user: logical[1..].to_vec(),
},
VAL_META_TTL if logical.len() >= 9 => {
let expire = u64::from_le_bytes(logical[1..9].try_into().unwrap());
ValueMeta::Ttl {
expire_unix_secs: expire,
user: logical[9..].to_vec(),
}
}
_ => ValueMeta::Plain {
user: logical.to_vec(),
},
}
}
pub fn logical_to_user(logical: Vec<u8>) -> Option<Vec<u8>> {
let meta = decode_value_meta(&logical);
if meta.is_expired_at(now_unix_secs()) {
None
} else {
Some(meta.user().to_vec())
}
}
pub(crate) fn parse_i64_user(user: &[u8]) -> Result<i64, IncrError> {
let s = std::str::from_utf8(user).map_err(|_| IncrError::NotInteger)?;
let s = s.trim();
if s.is_empty() {
return Err(IncrError::NotInteger);
}
s.parse::<i64>().map_err(|_| IncrError::NotInteger)
}
pub fn key_matches(key: &[u8], pattern: &[u8], mode: KeyMatchMode) -> bool {
if pattern.is_empty() {
return true;
}
match mode {
KeyMatchMode::Contains => {
if pattern.len() > key.len() {
return false;
}
key.windows(pattern.len()).any(|w| w == pattern)
}
KeyMatchMode::Prefix => key.starts_with(pattern),
}
}
pub fn prefix_upper_bound(prefix: &[u8]) -> Option<Vec<u8>> {
if prefix.is_empty() {
return None;
}
let mut u = prefix.to_vec();
while let Some(last) = u.last_mut() {
if *last < 0xFF {
*last += 1;
return Some(u);
}
u.pop();
}
None
}