wedb_embed 0.1.0

Embedded Kvrocks-compatible storage engine for WeDb
Documentation
use std::fmt;
use std::str::FromStr;

use serde::{Deserialize, Serialize};

use crate::error::{Error, Result};

/// BITOP 运算类型(对标 Apache Kvrocks BitOpFlags)
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum BitOp {
    And,
    Or,
    Xor,
    Not,
}

impl BitOp {
    #[inline]
    pub const fn as_str(&self) -> &'static str {
        match self {
            Self::And => "AND",
            Self::Or => "OR",
            Self::Xor => "XOR",
            Self::Not => "NOT",
        }
    }
}

impl FromStr for BitOp {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self> {
        match s.to_ascii_uppercase().as_str() {
            "AND" => Ok(Self::And),
            "OR" => Ok(Self::Or),
            "XOR" => Ok(Self::Xor),
            "NOT" => Ok(Self::Not),
            _ => Err(Error::invalid_data(format!("ERR unknown bitop: '{s}'"))),
        }
    }
}

impl fmt::Display for BitOp {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

/// BITFIELD 溢出处理策略(对标 Apache Kvrocks BitfieldOverflowBehavior)
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum BitfieldOverflow {
    #[default]
    Wrap,
    Sat,
    Fail,
}

impl BitfieldOverflow {
    #[inline]
    pub const fn as_str(&self) -> &'static str {
        match self {
            Self::Wrap => "WRAP",
            Self::Sat => "SAT",
            Self::Fail => "FAIL",
        }
    }
}

impl FromStr for BitfieldOverflow {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self> {
        match s.to_ascii_uppercase().as_str() {
            "WRAP" => Ok(Self::Wrap),
            "SAT" => Ok(Self::Sat),
            "FAIL" => Ok(Self::Fail),
            _ => Err(Error::invalid_data(format!(
                "ERR Invalid OVERFLOW type '{s}', must be WRAP, SAT or FAIL"
            ))),
        }
    }
}

impl fmt::Display for BitfieldOverflow {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

/// BITFIELD 整数类型编码(对标 Apache Kvrocks BitfieldEncoding)
/// 支持 i1~i64(有符号)与 u1~u63(无符号)
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum BitfieldEncoding {
    Signed(u8),
    Unsigned(u8),
}

impl BitfieldEncoding {
    #[inline]
    pub fn signed(bits: u8) -> Result<Self> {
        if (1..=64).contains(&bits) {
            Ok(Self::Signed(bits))
        } else {
            Err(Error::invalid_data(
                "ERR Invalid bitfield signed encoding bit length (1..=64)",
            ))
        }
    }

    #[inline]
    pub fn unsigned(bits: u8) -> Result<Self> {
        if (1..=63).contains(&bits) {
            Ok(Self::Unsigned(bits))
        } else {
            Err(Error::invalid_data(
                "ERR Invalid bitfield unsigned encoding bit length (1..=63)",
            ))
        }
    }

    #[inline]
    pub const fn is_signed(&self) -> bool {
        matches!(self, Self::Signed(_))
    }

    #[inline]
    pub const fn is_unsigned(&self) -> bool {
        matches!(self, Self::Unsigned(_))
    }

    #[inline]
    pub const fn bits(&self) -> u8 {
        match self {
            Self::Signed(b) | Self::Unsigned(b) => *b,
        }
    }
}

impl FromStr for BitfieldEncoding {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self> {
        let s = s.trim();
        let (first, num_str) = match s.chars().next() {
            Some(c @ ('i' | 'I' | 'u' | 'U')) => (c.to_ascii_lowercase(), &s[c.len_utf8()..]),
            _ => {
                return Err(Error::invalid_data(format!(
                    "ERR Invalid bitfield type prefix in '{s}', must start with 'i' or 'u'"
                )));
            }
        };
        let bits = num_str
            .parse::<u8>()
            .map_err(|_| Error::invalid_data(format!("ERR invalid bitfield bits in '{s}'")))?;

        match first {
            'i' => Self::signed(bits),
            'u' => Self::unsigned(bits),
            _ => unreachable!(),
        }
    }
}

impl fmt::Display for BitfieldEncoding {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Signed(b) => write!(f, "i{b}"),
            Self::Unsigned(b) => write!(f, "u{b}"),
        }
    }
}

/// BITFIELD 单项操作类型
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum BitfieldOpType {
    Get,
    Set,
    IncrBy,
}

/// BITFIELD 操作指令(对标 Apache Kvrocks BitfieldOperation)
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BitfieldOperation {
    pub op_type: BitfieldOpType,
    pub encoding: BitfieldEncoding,
    pub offset: u64,
    pub value: i64,
    pub overflow: BitfieldOverflow,
}

impl BitfieldOperation {
    #[inline]
    pub const fn get(encoding: BitfieldEncoding, offset: u64) -> Self {
        Self {
            op_type: BitfieldOpType::Get,
            encoding,
            offset,
            value: 0,
            overflow: BitfieldOverflow::Wrap,
        }
    }

    #[inline]
    pub const fn set(
        encoding: BitfieldEncoding,
        offset: u64,
        value: i64,
        overflow: BitfieldOverflow,
    ) -> Self {
        Self {
            op_type: BitfieldOpType::Set,
            encoding,
            offset,
            value,
            overflow,
        }
    }

    #[inline]
    pub const fn incrby(
        encoding: BitfieldEncoding,
        offset: u64,
        increment: i64,
        overflow: BitfieldOverflow,
    ) -> Self {
        Self {
            op_type: BitfieldOpType::IncrBy,
            encoding,
            offset,
            value: increment,
            overflow,
        }
    }
}

/// BITFIELD 操作返回值(对标 Apache Kvrocks BitfieldValue)
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum BitfieldValue {
    Signed(i64),
    Unsigned(u64),
}

impl BitfieldValue {
    #[inline]
    pub const fn as_i64(&self) -> i64 {
        match self {
            Self::Signed(v) => *v,
            Self::Unsigned(v) => *v as i64,
        }
    }

    #[inline]
    pub const fn as_u64(&self) -> u64 {
        match self {
            Self::Signed(v) => *v as u64,
            Self::Unsigned(v) => *v,
        }
    }
}

impl PartialEq<i64> for BitfieldValue {
    #[inline]
    fn eq(&self, other: &i64) -> bool {
        self.as_i64() == *other
    }
}

impl PartialEq<u64> for BitfieldValue {
    #[inline]
    fn eq(&self, other: &u64) -> bool {
        self.as_u64() == *other
    }
}

impl PartialEq<BitfieldValue> for i64 {
    #[inline]
    fn eq(&self, other: &BitfieldValue) -> bool {
        *self == other.as_i64()
    }
}

impl PartialEq<BitfieldValue> for u64 {
    #[inline]
    fn eq(&self, other: &BitfieldValue) -> bool {
        *self == other.as_u64()
    }
}

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

    #[test]
    fn test_bitop_parsing() {
        assert_eq!("AND".parse::<BitOp>().unwrap(), BitOp::And);
        assert_eq!("or".parse::<BitOp>().unwrap(), BitOp::Or);
        assert_eq!("XOR".parse::<BitOp>().unwrap(), BitOp::Xor);
        assert_eq!("not".parse::<BitOp>().unwrap(), BitOp::Not);
        assert!("INVALID".parse::<BitOp>().is_err());
    }

    #[test]
    fn test_overflow_parsing() {
        assert_eq!(
            "WRAP".parse::<BitfieldOverflow>().unwrap(),
            BitfieldOverflow::Wrap
        );
        assert_eq!(
            "sat".parse::<BitfieldOverflow>().unwrap(),
            BitfieldOverflow::Sat
        );
        assert_eq!(
            "FAIL".parse::<BitfieldOverflow>().unwrap(),
            BitfieldOverflow::Fail
        );
        assert!("NONE".parse::<BitfieldOverflow>().is_err());
    }

    #[test]
    fn test_bitfield_encoding() {
        assert_eq!(
            "i1".parse::<BitfieldEncoding>().unwrap(),
            BitfieldEncoding::Signed(1)
        );
        assert_eq!(
            "i64".parse::<BitfieldEncoding>().unwrap(),
            BitfieldEncoding::Signed(64)
        );
        assert!("i65".parse::<BitfieldEncoding>().is_err());
        assert!("i0".parse::<BitfieldEncoding>().is_err());

        assert_eq!(
            "u1".parse::<BitfieldEncoding>().unwrap(),
            BitfieldEncoding::Unsigned(1)
        );
        assert_eq!(
            "u63".parse::<BitfieldEncoding>().unwrap(),
            BitfieldEncoding::Unsigned(63)
        );
        assert!("u64".parse::<BitfieldEncoding>().is_err());
        assert!("u0".parse::<BitfieldEncoding>().is_err());

        let enc_s = BitfieldEncoding::signed(32).unwrap();
        assert_eq!(enc_s.to_string(), "i32");
        let enc_u = BitfieldEncoding::unsigned(16).unwrap();
        assert_eq!(enc_u.to_string(), "u16");
    }
}