Skip to main content

cloudreve_sdk_api/boolset/
mod.rs

1use base64::{engine::general_purpose::STANDARD, Engine};
2
3/// A compact boolean set stored as a bit array
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub struct Boolset {
6    data: Vec<u8>,
7}
8
9impl Boolset {
10    /// Create a new empty Boolset
11    pub fn new() -> Self {
12        Self { data: Vec::new() }
13    }
14
15    /// Create a Boolset from raw bytes
16    pub fn from_raw(data: Vec<u8>) -> Self {
17        Self { data }
18    }
19
20    /// Create a Boolset from a base64-encoded string
21    pub fn from_base64(encoded: &str) -> Result<Self, base64::DecodeError> {
22        let data = STANDARD.decode(encoded)?;
23        Ok(Self { data })
24    }
25
26    /// Create a Boolset from an optional base64 string, falling back to raw bytes or empty
27    /// This mimics the TypeScript constructor behavior
28    pub fn from_data(base64_str: Option<&str>, raw: Option<Vec<u8>>) -> Self {
29        if let Some(encoded) = base64_str {
30            Self::from_base64(encoded).unwrap_or_else(|e| {
31                eprintln!("Failed to decode boolset: {}", e);
32                Self::new()
33            })
34        } else if let Some(data) = raw {
35            Self::from_raw(data)
36        } else {
37            Self::new()
38        }
39    }
40
41    /// Check if a bit at the given index is enabled
42    pub fn enabled(&self, index: usize) -> bool {
43        if index >= self.data.len() * 8 {
44            return false;
45        }
46        (self.data[index / 8] & (1 << (index % 8))) != 0
47    }
48
49    /// Perform bitwise AND with another Boolset, returning a new Boolset
50    pub fn and(&self, other: &Boolset) -> Boolset {
51        let length = self.data.len().max(other.data.len());
52        let mut result = vec![0u8; length];
53
54        for i in 0..length {
55            let a = self.data.get(i).copied().unwrap_or(0);
56            let b = other.data.get(i).copied().unwrap_or(0);
57            result[i] = a & b;
58        }
59
60        Boolset { data: result }
61    }
62
63    /// Perform bitwise OR with another Boolset, returning a new Boolset
64    pub fn or(&self, other: &Boolset) -> Boolset {
65        let length = self.data.len().max(other.data.len());
66        let mut result = vec![0u8; length];
67
68        for i in 0..length {
69            let a = self.data.get(i).copied().unwrap_or(0);
70            let b = other.data.get(i).copied().unwrap_or(0);
71            result[i] = a | b;
72        }
73
74        Boolset { data: result }
75    }
76
77    /// Set or clear a bit at the given index
78    /// Returns a mutable reference to self for method chaining
79    pub fn set(&mut self, index: usize, enabled: bool) -> &mut Self {
80        let byte_index = index / 8;
81        let bit_index = index % 8;
82
83        // Expand array if necessary
84        if byte_index >= self.data.len() {
85            self.data.resize(byte_index + 1, 0);
86        }
87
88        if enabled {
89            self.data[byte_index] |= 1 << bit_index;
90        } else {
91            self.data[byte_index] &= !(1 << bit_index);
92        }
93
94        self
95    }
96
97    /// Set multiple bits at once from a slice of (index, enabled) tuples
98    pub fn sets(&mut self, values: &[(usize, bool)]) -> &mut Self {
99        for &(index, enabled) in values {
100            self.set(index, enabled);
101        }
102        self
103    }
104
105    /// Convert to base64-encoded string
106    pub fn to_base64(&self) -> String {
107        STANDARD.encode(&self.data)
108    }
109
110    /// Get the underlying byte data
111    pub fn as_bytes(&self) -> &[u8] {
112        &self.data
113    }
114}
115
116impl Default for Boolset {
117    fn default() -> Self {
118        Self::new()
119    }
120}