use bitcode::{Decode, Encode};
use strum::{AsRefStr, Display, FromRepr, IntoStaticStr};
use crate::error::{Error, Result};
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
Hash,
PartialOrd,
Ord,
FromRepr,
Display,
AsRefStr,
IntoStaticStr,
Encode,
Decode,
)]
#[repr(u8)]
pub enum BfTag {
ZMember = 0,
ZScore = 1,
NextNamespace = 32,
AclUser = 33,
AclMeta = 34,
ClusterMeta = 35,
ReplMeta = 36,
}
impl BfTag {
pub const TAG_LEN: usize = 1;
pub const BUSINESS_TAG_MAX: u8 = 31;
pub const SYSTEM_TAG_BASE: u8 = 32;
pub const SYSTEM_TAG_MAX: u8 = 63;
pub const STACK_KEY_CAP: usize = 64;
#[inline(always)]
pub const fn from_u8(val: u8) -> Option<Self> {
Self::from_repr(val)
}
#[inline(always)]
pub const fn as_u8(self) -> u8 {
self as u8
}
#[inline(always)]
pub const fn as_str(self) -> &'static str {
match self {
Self::ZMember => "ZMember",
Self::ZScore => "ZScore",
Self::NextNamespace => "NextNamespace",
Self::AclUser => "AclUser",
Self::AclMeta => "AclMeta",
Self::ClusterMeta => "ClusterMeta",
Self::ReplMeta => "ReplMeta",
}
}
#[inline(always)]
pub const fn prefix(self) -> [u8; Self::TAG_LEN] {
[self as u8]
}
#[inline(always)]
pub const fn is_business(self) -> bool {
(self as u8) <= Self::BUSINESS_TAG_MAX
}
#[inline(always)]
pub const fn is_system(self) -> bool {
(self as u8) >= Self::SYSTEM_TAG_BASE
}
#[inline(always)]
pub const fn is_zset(self) -> bool {
matches!(self, Self::ZMember | Self::ZScore)
}
#[inline]
pub fn with_key<R>(self, sub_key: &[u8], f: impl FnOnce(&[u8]) -> R) -> R {
let total_len = Self::TAG_LEN + sub_key.len();
if total_len <= Self::STACK_KEY_CAP {
let mut buf = [0u8; Self::STACK_KEY_CAP];
buf[0] = self as u8;
buf[1..total_len].copy_from_slice(sub_key);
f(&buf[..total_len])
} else {
let mut vec = Vec::with_capacity(total_len);
vec.push(self as u8);
vec.extend_from_slice(sub_key);
f(&vec)
}
}
#[inline]
pub fn encode_key(self, sub_key: impl AsRef<[u8]>) -> Vec<u8> {
let sub = sub_key.as_ref();
let total_len = Self::TAG_LEN + sub.len();
let mut key = Vec::with_capacity(total_len);
key.push(self as u8);
key.extend_from_slice(sub);
key
}
#[inline(always)]
pub const fn strip_prefix(self, key: &[u8]) -> Option<&[u8]> {
match key {
[first, rest @ ..] if *first == self as u8 => Some(rest),
_ => None,
}
}
}
impl TryFrom<u8> for BfTag {
type Error = Error;
#[inline]
fn try_from(val: u8) -> Result<Self> {
Self::from_repr(val).ok_or(Error::InvalidKeyTag(val))
}
}
impl From<BfTag> for u8 {
#[inline(always)]
fn from(tag: BfTag) -> Self {
tag as Self
}
}