use base64::{engine::general_purpose::STANDARD, Engine};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Boolset {
data: Vec<u8>,
}
impl Boolset {
pub fn new() -> Self {
Self { data: Vec::new() }
}
pub fn from_raw(data: Vec<u8>) -> Self {
Self { data }
}
pub fn from_base64(encoded: &str) -> Result<Self, base64::DecodeError> {
let data = STANDARD.decode(encoded)?;
Ok(Self { data })
}
pub fn from_data(base64_str: Option<&str>, raw: Option<Vec<u8>>) -> Self {
if let Some(encoded) = base64_str {
Self::from_base64(encoded).unwrap_or_else(|e| {
eprintln!("Failed to decode boolset: {}", e);
Self::new()
})
} else if let Some(data) = raw {
Self::from_raw(data)
} else {
Self::new()
}
}
pub fn enabled(&self, index: usize) -> bool {
if index >= self.data.len() * 8 {
return false;
}
(self.data[index / 8] & (1 << (index % 8))) != 0
}
pub fn and(&self, other: &Boolset) -> Boolset {
let length = self.data.len().max(other.data.len());
let mut result = vec![0u8; length];
for i in 0..length {
let a = self.data.get(i).copied().unwrap_or(0);
let b = other.data.get(i).copied().unwrap_or(0);
result[i] = a & b;
}
Boolset { data: result }
}
pub fn or(&self, other: &Boolset) -> Boolset {
let length = self.data.len().max(other.data.len());
let mut result = vec![0u8; length];
for i in 0..length {
let a = self.data.get(i).copied().unwrap_or(0);
let b = other.data.get(i).copied().unwrap_or(0);
result[i] = a | b;
}
Boolset { data: result }
}
pub fn set(&mut self, index: usize, enabled: bool) -> &mut Self {
let byte_index = index / 8;
let bit_index = index % 8;
if byte_index >= self.data.len() {
self.data.resize(byte_index + 1, 0);
}
if enabled {
self.data[byte_index] |= 1 << bit_index;
} else {
self.data[byte_index] &= !(1 << bit_index);
}
self
}
pub fn sets(&mut self, values: &[(usize, bool)]) -> &mut Self {
for &(index, enabled) in values {
self.set(index, enabled);
}
self
}
pub fn to_base64(&self) -> String {
STANDARD.encode(&self.data)
}
pub fn as_bytes(&self) -> &[u8] {
&self.data
}
}
impl Default for Boolset {
fn default() -> Self {
Self::new()
}
}