1use std::{fmt, str::FromStr};
2
3use crate::error::{Error, Result};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
8pub enum BitUnit {
9 #[default]
10 Byte,
11 Bit,
12}
13
14#[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#[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#[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#[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#[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 #[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#[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#[derive(Debug, Clone, Copy, PartialEq, Eq, bitcode::Encode, bitcode::Decode)]
257pub enum BitfieldOpType {
258 Get,
259 Set,
260 IncrBy,
261}
262
263#[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#[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}