Skip to main content

wedb_embed/api/bitmap/
opt.rs

1use std::{fmt, str::FromStr};
2
3use crate::error::{Error, Result};
4
5/// Operation definition.
6/// BITCOUNT / BITPOS 位图索引单位
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
8pub enum BitUnit {
9  #[default]
10  Byte,
11  Bit,
12}
13
14/// BITCOUNT command options enumeration.
15/// BITCOUNT 选项枚举
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum BitCount {
18  Range(i64, i64),
19  Start(i64),
20  End(i64),
21  Unit(BitUnit),
22}
23
24/// BITPOS command options enumeration.
25/// BITPOS 选项枚举
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum BitPos {
28  Range(i64, i64),
29  Start(i64),
30  End(i64),
31  Unit(BitUnit),
32}
33
34/// Domain operation (aligned with Apache Kvrocks BitOpFlags).
35/// BITOP 运算类型(对标 Apache Kvrocks BitOpFlags)
36#[derive(Debug, Clone, Copy, PartialEq, Eq, bitcode::Encode, bitcode::Decode)]
37pub enum BitOp {
38  And,
39  Or,
40  Xor,
41  Not,
42}
43
44impl BitOp {
45  #[inline]
46  pub const fn as_str(&self) -> &'static str {
47    match self {
48      Self::And => "AND",
49      Self::Or => "OR",
50      Self::Xor => "XOR",
51      Self::Not => "NOT",
52    }
53  }
54}
55
56impl FromStr for BitOp {
57  type Err = Error;
58
59  #[inline]
60  fn from_str(s: &str) -> Result<Self> {
61    if s.eq_ignore_ascii_case("AND") {
62      Ok(Self::And)
63    } else if s.eq_ignore_ascii_case("OR") {
64      Ok(Self::Or)
65    } else if s.eq_ignore_ascii_case("XOR") {
66      Ok(Self::Xor)
67    } else if s.eq_ignore_ascii_case("NOT") {
68      Ok(Self::Not)
69    } else {
70      Err(Error::invalid_data(format!("ERR unknown bitop: '{s}'")))
71    }
72  }
73}
74
75impl fmt::Display for BitOp {
76  #[inline]
77  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78    write!(f, "{}", self.as_str())
79  }
80}
81
82/// Domain operation (aligned with Apache Kvrocks BitfieldOverflowBehavior).
83/// BITFIELD 溢出处理策略(对标 Apache Kvrocks BitfieldOverflowBehavior)
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, bitcode::Encode, bitcode::Decode)]
85pub enum BitfieldOverflow {
86  #[default]
87  Wrap,
88  Sat,
89  Fail,
90}
91
92impl BitfieldOverflow {
93  #[inline]
94  pub const fn as_str(&self) -> &'static str {
95    match self {
96      Self::Wrap => "WRAP",
97      Self::Sat => "SAT",
98      Self::Fail => "FAIL",
99    }
100  }
101}
102
103impl FromStr for BitfieldOverflow {
104  type Err = Error;
105
106  #[inline]
107  fn from_str(s: &str) -> Result<Self> {
108    if s.eq_ignore_ascii_case("WRAP") {
109      Ok(Self::Wrap)
110    } else if s.eq_ignore_ascii_case("SAT") {
111      Ok(Self::Sat)
112    } else if s.eq_ignore_ascii_case("FAIL") {
113      Ok(Self::Fail)
114    } else {
115      Err(Error::invalid_data(format!(
116        "ERR Invalid OVERFLOW type '{s}', must be WRAP, SAT or FAIL"
117      )))
118    }
119  }
120}
121
122impl fmt::Display for BitfieldOverflow {
123  #[inline]
124  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125    write!(f, "{}", self.as_str())
126  }
127}
128
129/// Encodes data into binary format.
130/// BITFIELD 整数类型编码(对标 Apache Kvrocks BitfieldEncoding)
131/// Operation definition.
132/// 支持 i1~i64(有符号)与 u1~u63(无符号)
133#[derive(Debug, Clone, Copy, PartialEq, Eq, bitcode::Encode, bitcode::Decode)]
134pub enum BitfieldEncoding {
135  Signed(u8),
136  Unsigned(u8),
137}
138
139impl BitfieldEncoding {
140  #[inline]
141  pub fn signed(bits: u8) -> Result<Self> {
142    if (1..=64).contains(&bits) {
143      Ok(Self::Signed(bits))
144    } else {
145      Err(Error::invalid_data(
146        "ERR Invalid bitfield signed encoding bit length (1..=64)",
147      ))
148    }
149  }
150
151  #[inline]
152  pub fn unsigned(bits: u8) -> Result<Self> {
153    if (1..=63).contains(&bits) {
154      Ok(Self::Unsigned(bits))
155    } else {
156      Err(Error::invalid_data(
157        "ERR Invalid bitfield unsigned encoding bit length (1..=63)",
158      ))
159    }
160  }
161
162  #[inline]
163  pub const fn is_signed(&self) -> bool {
164    matches!(self, Self::Signed(_))
165  }
166
167  #[inline]
168  pub const fn is_unsigned(&self) -> bool {
169    matches!(self, Self::Unsigned(_))
170  }
171
172  #[inline]
173  pub const fn bits(&self) -> u8 {
174    match self {
175      Self::Signed(b) | Self::Unsigned(b) => *b,
176    }
177  }
178
179  /// Returns or computes calculated value.
180  /// 根据位置索引 #N 计算绝对位偏移(对标 Redis / Kvrocks #N 语法)
181  #[inline]
182  pub fn positional_offset(&self, index: u64) -> Result<u64> {
183    let bits = self.bits() as u64;
184    index
185      .checked_mul(bits)
186      .filter(|&off| off <= u32::MAX as u64)
187      .ok_or_else(|| Error::invalid_data("ERR bit offset is not an integer or out of range"))
188  }
189}
190
191impl FromStr for BitfieldEncoding {
192  type Err = Error;
193
194  #[inline]
195  fn from_str(s: &str) -> Result<Self> {
196    let s = s.trim();
197    let bytes = s.as_bytes();
198    if bytes.is_empty() {
199      return Err(Error::invalid_data(
200        "ERR Invalid bitfield type: empty string",
201      ));
202    }
203
204    let prefix = bytes[0].to_ascii_lowercase();
205    let num_str = &s[1..];
206    let bits = num_str
207      .parse::<u8>()
208      .map_err(|_| Error::invalid_data(format!("ERR invalid bitfield bits in '{s}'")))?;
209
210    match prefix {
211      b'i' => Self::signed(bits),
212      b'u' => Self::unsigned(bits),
213      _ => Err(Error::invalid_data(format!(
214        "ERR Invalid bitfield type prefix in '{s}', must start with 'i' or 'u'"
215      ))),
216    }
217  }
218}
219
220impl fmt::Display for BitfieldEncoding {
221  #[inline]
222  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223    match self {
224      Self::Signed(b) => write!(f, "i{b}"),
225      Self::Unsigned(b) => write!(f, "u{b}"),
226    }
227  }
228}
229
230/// Parses parameter or binary slice.
231/// 解析 BITFIELD 偏移量参数字符串(支持普通数字 "100" 以及位置索引 "#5",对标 Kvrocks)
232#[inline]
233pub fn parse_bitfield_offset(offset_str: &str, encoding: BitfieldEncoding) -> Result<u64> {
234  let s = offset_str.trim();
235  if let Some(pos_str) = s.strip_prefix('#') {
236    let idx = pos_str
237      .parse::<u64>()
238      .map_err(|_| Error::invalid_data("ERR bit offset is not an integer or out of range"))?;
239    encoding.positional_offset(idx)
240  } else {
241    let off = s
242      .parse::<u64>()
243      .map_err(|_| Error::invalid_data("ERR bit offset is not an integer or out of range"))?;
244    if off <= u32::MAX as u64 {
245      Ok(off)
246    } else {
247      Err(Error::invalid_data(
248        "ERR bit offset is not an integer or out of range",
249      ))
250    }
251  }
252}
253
254/// Operation definition.
255/// BITFIELD 单项操作类型
256#[derive(Debug, Clone, Copy, PartialEq, Eq, bitcode::Encode, bitcode::Decode)]
257pub enum BitfieldOpType {
258  Get,
259  Set,
260  IncrBy,
261}
262
263/// Domain operation (aligned with Apache Kvrocks BitfieldOperation).
264/// BITFIELD 操作指令(对标 Apache Kvrocks BitfieldOperation)
265#[derive(Debug, Clone, PartialEq, Eq, bitcode::Encode, bitcode::Decode)]
266pub struct BitfieldOperation {
267  pub op_type: BitfieldOpType,
268  pub encoding: BitfieldEncoding,
269  pub offset: u64,
270  pub value: i64,
271  pub overflow: BitfieldOverflow,
272}
273
274impl BitfieldOperation {
275  #[inline]
276  pub const fn get(encoding: BitfieldEncoding, offset: u64) -> Self {
277    Self {
278      op_type: BitfieldOpType::Get,
279      encoding,
280      offset,
281      value: 0,
282      overflow: BitfieldOverflow::Wrap,
283    }
284  }
285
286  #[inline]
287  pub fn get_positional(encoding: BitfieldEncoding, index: u64) -> Result<Self> {
288    let offset = encoding.positional_offset(index)?;
289    Ok(Self::get(encoding, offset))
290  }
291
292  #[inline]
293  pub const fn set(
294    encoding: BitfieldEncoding,
295    offset: u64,
296    value: i64,
297    overflow: BitfieldOverflow,
298  ) -> Self {
299    Self {
300      op_type: BitfieldOpType::Set,
301      encoding,
302      offset,
303      value,
304      overflow,
305    }
306  }
307
308  #[inline]
309  pub fn set_positional(
310    encoding: BitfieldEncoding,
311    index: u64,
312    value: i64,
313    overflow: BitfieldOverflow,
314  ) -> Result<Self> {
315    let offset = encoding.positional_offset(index)?;
316    Ok(Self::set(encoding, offset, value, overflow))
317  }
318
319  #[inline]
320  pub const fn incrby(
321    encoding: BitfieldEncoding,
322    offset: u64,
323    increment: i64,
324    overflow: BitfieldOverflow,
325  ) -> Self {
326    Self {
327      op_type: BitfieldOpType::IncrBy,
328      encoding,
329      offset,
330      value: increment,
331      overflow,
332    }
333  }
334
335  #[inline]
336  pub fn incrby_positional(
337    encoding: BitfieldEncoding,
338    index: u64,
339    increment: i64,
340    overflow: BitfieldOverflow,
341  ) -> Result<Self> {
342    let offset = encoding.positional_offset(index)?;
343    Ok(Self::incrby(encoding, offset, increment, overflow))
344  }
345}
346
347/// Domain operation (aligned with Apache Kvrocks BitfieldValue).
348/// BITFIELD 操作返回值(对标 Apache Kvrocks BitfieldValue)
349#[derive(Debug, Clone, Copy, PartialEq, Eq, bitcode::Encode, bitcode::Decode)]
350pub enum BitfieldValue {
351  Signed(i64),
352  Unsigned(u64),
353}
354
355impl BitfieldValue {
356  #[inline]
357  pub const fn as_i64(&self) -> i64 {
358    match self {
359      Self::Signed(v) => *v,
360      Self::Unsigned(v) => *v as i64,
361    }
362  }
363
364  #[inline]
365  pub const fn as_u64(&self) -> u64 {
366    match self {
367      Self::Signed(v) => *v as u64,
368      Self::Unsigned(v) => *v,
369    }
370  }
371}
372
373impl PartialEq<i64> for BitfieldValue {
374  #[inline]
375  fn eq(&self, other: &i64) -> bool {
376    self.as_i64() == *other
377  }
378}
379
380impl PartialEq<u64> for BitfieldValue {
381  #[inline]
382  fn eq(&self, other: &u64) -> bool {
383    self.as_u64() == *other
384  }
385}
386
387impl PartialEq<BitfieldValue> for i64 {
388  #[inline]
389  fn eq(&self, other: &BitfieldValue) -> bool {
390    *self == other.as_i64()
391  }
392}
393
394impl PartialEq<BitfieldValue> for u64 {
395  #[inline]
396  fn eq(&self, other: &BitfieldValue) -> bool {
397    *self == other.as_u64()
398  }
399}