wedb_embed 0.1.0

Embedded Kvrocks-compatible storage engine for WeDb
Documentation
use crate::meta::{KeyMeta, RedisType};
use serde::{Deserialize, Serialize};

/// 集合结构元数据(对标 Apache Kvrocks SetMetadata 26字节 / 紧凑25字节)
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct SetMeta {
    pub base: KeyMeta,
}

impl SetMeta {
    pub const ENCODED_SIZE: usize = KeyMeta::ENCODED_SIZE;
    pub const KVROCKS_ENCODED_SIZE: usize = KeyMeta::KVROCKS_COMPLEX_ENCODED_SIZE;

    #[inline]
    pub fn new(expire_at: u64, version: u64, size: u64) -> Self {
        Self {
            base: KeyMeta::new(RedisType::Set, expire_at, version, size),
        }
    }

    #[inline]
    pub fn new_with_version(expire_at: u64, size: u64) -> Self {
        Self {
            base: KeyMeta::new_with_version(RedisType::Set, expire_at, size),
        }
    }

    #[inline]
    pub fn size(&self) -> u64 {
        self.base.size
    }

    #[inline]
    pub fn version(&self) -> u64 {
        self.base.version
    }

    #[inline]
    pub fn expire_at(&self) -> u64 {
        self.base.expire_at
    }

    #[inline]
    pub fn ttl(&self, now_ms: u64) -> i64 {
        self.base.ttl(now_ms)
    }

    #[inline]
    pub fn is_empty(&self) -> bool {
        self.base.size == 0
    }

    #[inline]
    pub fn is_expired(&self, now_ms: u64) -> bool {
        self.base.is_expired(now_ms)
    }

    #[inline]
    pub fn encode(&self) -> [u8; Self::ENCODED_SIZE] {
        self.base.encode()
    }

    #[inline]
    pub fn encode_kvrocks(&self) -> Vec<u8> {
        self.base.encode_kvrocks()
    }

    #[inline]
    pub fn decode(bytes: &[u8]) -> Option<Self> {
        let base = KeyMeta::decode(bytes)?;
        if base.rtype == RedisType::Set {
            Some(Self { base })
        } else {
            None
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_set_meta_roundtrip() {
        let meta = SetMeta::new(1700000000000, 101, 55);
        let enc = meta.encode();
        assert_eq!(enc.len(), SetMeta::ENCODED_SIZE);

        let dec = SetMeta::decode(&enc).expect("decode failed");
        assert_eq!(dec.base.rtype, RedisType::Set);
        assert_eq!(dec.base.expire_at, 1700000000000);
        assert_eq!(dec.base.version, 101);
        assert_eq!(dec.base.size, 55);
        assert_eq!(dec.size(), 55);
        assert_eq!(dec.version(), 101);
        assert_eq!(dec.expire_at(), 1700000000000);
        assert!(!dec.is_empty());

        let kv_enc = meta.encode_kvrocks();
        assert_eq!(kv_enc.len(), SetMeta::KVROCKS_ENCODED_SIZE);
        let kv_dec = SetMeta::decode(&kv_enc).expect("decode kvrocks failed");
        assert_eq!(kv_dec.base.rtype, RedisType::Set);
        assert_eq!(kv_dec.base.expire_at, 1700000000000);
        assert_eq!(kv_dec.base.version, 101);
        assert_eq!(kv_dec.base.size, 55);

        // 测试 new_with_version 与过期时间判断
        let auto_meta = SetMeta::new_with_version(2000, 10);
        assert_eq!(auto_meta.size(), 10);
        assert!(auto_meta.version() > 0);
        assert!(!auto_meta.is_expired(1000));
        assert!(auto_meta.is_expired(2000));
        assert!(auto_meta.is_expired(3000));
        assert_eq!(auto_meta.ttl(1000), 1000);
        assert_eq!(auto_meta.ttl(3000), -2);

        let empty_meta = SetMeta::new(0, 1, 0);
        assert!(empty_meta.is_empty());
        assert!(empty_meta.is_expired(1000));
        assert_eq!(empty_meta.ttl(1000), -1);

        // 非 Set 类型元数据解码应返回 None
        let zset_meta = KeyMeta::new(RedisType::ZSet, 0, 1, 10);
        assert!(SetMeta::decode(&zset_meta.encode()).is_none());

        // 截断数据解码应返回 None
        assert!(SetMeta::decode(&[0u8; 5]).is_none());
    }
}