use std::fmt;
use std::str::FromStr;
use serde::{Deserialize, Serialize};
use crate::error::{Error, Result};
#[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())
}
}
#[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())
}
}
#[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}"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum BitfieldOpType {
Get,
Set,
IncrBy,
}
#[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,
}
}
}
#[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");
}
}