use alloc::vec::Vec;
use bitflags::bitflags;
use datasize::DataSize;
use rand::{
distributions::{Distribution, Standard},
Rng,
};
use serde::{de::Error as SerdeError, Deserialize, Deserializer, Serialize, Serializer};
use crate::bytesrepr;
pub const ACCESS_RIGHTS_SERIALIZED_LENGTH: usize = 1;
bitflags! {
#[allow(clippy::derive_hash_xor_eq)]
#[derive(DataSize)]
pub struct AccessRights: u8 {
const NONE = 0;
const READ = 0b001;
const WRITE = 0b010;
const ADD = 0b100;
const READ_ADD = Self::READ.bits | Self::ADD.bits;
const READ_WRITE = Self::READ.bits | Self::WRITE.bits;
const ADD_WRITE = Self::ADD.bits | Self::WRITE.bits;
const READ_ADD_WRITE = Self::READ.bits | Self::ADD.bits | Self::WRITE.bits;
}
}
impl Default for AccessRights {
fn default() -> Self {
AccessRights::NONE
}
}
impl AccessRights {
pub fn is_readable(self) -> bool {
self & AccessRights::READ == AccessRights::READ
}
pub fn is_writeable(self) -> bool {
self & AccessRights::WRITE == AccessRights::WRITE
}
pub fn is_addable(self) -> bool {
self & AccessRights::ADD == AccessRights::ADD
}
pub fn is_none(self) -> bool {
self == AccessRights::NONE
}
}
impl core::fmt::Display for AccessRights {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match *self {
AccessRights::NONE => write!(f, "NONE"),
AccessRights::READ => write!(f, "READ"),
AccessRights::WRITE => write!(f, "WRITE"),
AccessRights::ADD => write!(f, "ADD"),
AccessRights::READ_ADD => write!(f, "READ_ADD"),
AccessRights::READ_WRITE => write!(f, "READ_WRITE"),
AccessRights::ADD_WRITE => write!(f, "ADD_WRITE"),
AccessRights::READ_ADD_WRITE => write!(f, "READ_ADD_WRITE"),
_ => write!(f, "UNKNOWN"),
}
}
}
impl bytesrepr::ToBytes for AccessRights {
fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
self.bits.to_bytes()
}
fn serialized_length(&self) -> usize {
ACCESS_RIGHTS_SERIALIZED_LENGTH
}
}
impl bytesrepr::FromBytes for AccessRights {
fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
let (id, rem) = u8::from_bytes(bytes)?;
match AccessRights::from_bits(id) {
Some(rights) => Ok((rights, rem)),
None => Err(bytesrepr::Error::Formatting),
}
}
}
impl Serialize for AccessRights {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.bits.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for AccessRights {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let bits = u8::deserialize(deserializer)?;
AccessRights::from_bits(bits).ok_or_else(|| SerdeError::custom("invalid bits"))
}
}
impl Distribution<AccessRights> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> AccessRights {
let mut result = AccessRights::NONE;
if rng.gen() {
result |= AccessRights::READ;
}
if rng.gen() {
result |= AccessRights::WRITE;
}
if rng.gen() {
result |= AccessRights::ADD;
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
fn test_readable(right: AccessRights, is_true: bool) {
assert_eq!(right.is_readable(), is_true)
}
#[test]
fn test_is_readable() {
test_readable(AccessRights::READ, true);
test_readable(AccessRights::READ_ADD, true);
test_readable(AccessRights::READ_WRITE, true);
test_readable(AccessRights::READ_ADD_WRITE, true);
test_readable(AccessRights::ADD, false);
test_readable(AccessRights::ADD_WRITE, false);
test_readable(AccessRights::WRITE, false);
}
fn test_writable(right: AccessRights, is_true: bool) {
assert_eq!(right.is_writeable(), is_true)
}
#[test]
fn test_is_writable() {
test_writable(AccessRights::WRITE, true);
test_writable(AccessRights::READ_WRITE, true);
test_writable(AccessRights::ADD_WRITE, true);
test_writable(AccessRights::READ, false);
test_writable(AccessRights::ADD, false);
test_writable(AccessRights::READ_ADD, false);
test_writable(AccessRights::READ_ADD_WRITE, true);
}
fn test_addable(right: AccessRights, is_true: bool) {
assert_eq!(right.is_addable(), is_true)
}
#[test]
fn test_is_addable() {
test_addable(AccessRights::ADD, true);
test_addable(AccessRights::READ_ADD, true);
test_addable(AccessRights::READ_WRITE, false);
test_addable(AccessRights::ADD_WRITE, true);
test_addable(AccessRights::READ, false);
test_addable(AccessRights::WRITE, false);
test_addable(AccessRights::READ_ADD_WRITE, true);
}
}