use crate::types::ValidationError;
use core::fmt;
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum AccessType {
None = 0b0000,
Read = 0b0001,
Write = 0b0010,
ReadWrite = 0b0011,
Execute = 0b0100,
ReadExecute = 0b0101,
WriteExecute = 0b0110,
ReadWriteExecute = 0b0111,
ReadPrivileged = 0b1001,
WritePrivileged = 0b1010,
ReadWritePrivileged = 0b1011,
ExecutePrivileged = 0b1100,
ReadExecutePrivileged = 0b1101,
WriteExecutePrivileged = 0b1110,
ReadWriteExecutePrivileged = 0b1111,
}
impl AccessType {
#[inline]
#[must_use]
pub const fn can_read(self) -> bool {
(self as u8 & 0b001) != 0
}
#[inline]
#[must_use]
pub const fn can_write(self) -> bool {
(self as u8 & 0b010) != 0
}
#[inline]
#[must_use]
pub const fn can_execute(self) -> bool {
(self as u8 & 0b100) != 0
}
#[inline]
#[must_use]
pub const fn is_privileged(self) -> bool {
(self as u8 & 0b1000) != 0
}
#[inline]
#[must_use]
pub const fn const_can_read(self) -> bool {
self.can_read()
}
#[inline]
#[must_use]
pub const fn const_can_write(self) -> bool {
self.can_write()
}
#[inline]
#[must_use]
pub const fn const_can_execute(self) -> bool {
self.can_execute()
}
#[inline]
#[must_use]
pub const fn has_permission(self, requested: Self) -> bool {
(self as u8 & requested as u8) == requested as u8
}
#[inline]
#[must_use]
pub const fn intersect(self, other: Self) -> Self {
let bits = self as u8 & other as u8;
Self::from_bits_unchecked(bits)
}
#[inline]
#[must_use]
pub const fn union(self, other: Self) -> Self {
let bits = self as u8 | other as u8;
Self::from_bits_unchecked(bits)
}
#[inline]
#[must_use]
pub const fn to_bits(self) -> u8 {
self as u8
}
#[inline]
pub const fn from_bits(bits: u8) -> Result<Self, ValidationError> {
match bits {
0b0000..=0b0111 | 0b1001..=0b1111 => {
Ok(Self::from_bits_unchecked(bits))
}
_ => Err(ValidationError::InvalidAccessType { bits }),
}
}
#[inline]
const fn from_bits_unchecked(bits: u8) -> Self {
match bits {
0b0000 => Self::None,
0b0001 => Self::Read,
0b0010 => Self::Write,
0b0011 => Self::ReadWrite,
0b0100 => Self::Execute,
0b0101 => Self::ReadExecute,
0b0110 => Self::WriteExecute,
0b0111 => Self::ReadWriteExecute,
0b1001 => Self::ReadPrivileged,
0b1010 => Self::WritePrivileged,
0b1011 => Self::ReadWritePrivileged,
0b1100 => Self::ExecutePrivileged,
0b1101 => Self::ReadExecutePrivileged,
0b1110 => Self::WriteExecutePrivileged,
0b1111 => Self::ReadWriteExecutePrivileged,
_ => Self::None, }
}
#[inline]
pub fn validate_against(self, available: Self) -> Result<(), ValidationError> {
if available.has_permission(self) {
Ok(())
} else {
Err(ValidationError::PermissionDenied {
requested: format!("{self}"),
available: format!("{available}"),
})
}
}
}
impl fmt::Display for AccessType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::None => "None",
Self::Read => "Read",
Self::Write => "Write",
Self::ReadWrite => "ReadWrite",
Self::Execute => "Execute",
Self::ReadExecute => "ReadExecute",
Self::WriteExecute => "WriteExecute",
Self::ReadWriteExecute => "ReadWriteExecute",
Self::ReadPrivileged => "ReadPrivileged",
Self::WritePrivileged => "WritePrivileged",
Self::ReadWritePrivileged => "ReadWritePrivileged",
Self::ExecutePrivileged => "ExecutePrivileged",
Self::ReadExecutePrivileged => "ReadExecutePrivileged",
Self::WriteExecutePrivileged => "WriteExecutePrivileged",
Self::ReadWriteExecutePrivileged => "ReadWriteExecutePrivileged",
};
write!(f, "{s}")
}
}
impl Default for AccessType {
fn default() -> Self {
Self::None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_access_type_bits() {
assert_eq!(AccessType::None.to_bits(), 0b000);
assert_eq!(AccessType::Read.to_bits(), 0b001);
assert_eq!(AccessType::Write.to_bits(), 0b010);
assert_eq!(AccessType::ReadWrite.to_bits(), 0b011);
assert_eq!(AccessType::Execute.to_bits(), 0b100);
assert_eq!(AccessType::ReadExecute.to_bits(), 0b101);
assert_eq!(AccessType::WriteExecute.to_bits(), 0b110);
assert_eq!(AccessType::ReadWriteExecute.to_bits(), 0b111);
}
#[test]
fn test_access_type_from_bits() {
for bits in 0..=0b111 {
let access = AccessType::from_bits(bits).unwrap();
assert_eq!(access.to_bits(), bits);
}
}
#[test]
fn test_permissions() {
assert!(AccessType::Read.can_read());
assert!(!AccessType::Read.can_write());
assert!(!AccessType::Read.can_execute());
assert!(AccessType::ReadWrite.can_read());
assert!(AccessType::ReadWrite.can_write());
assert!(!AccessType::ReadWrite.can_execute());
assert!(AccessType::ReadWriteExecute.can_read());
assert!(AccessType::ReadWriteExecute.can_write());
assert!(AccessType::ReadWriteExecute.can_execute());
}
}