use epics_base_rs::server::access_security::AccessLevel;
use crate::protocol::{ECA_NORDACCESS, ECA_NOWTACCESS};
#[derive(Debug, Clone, Copy)]
pub struct CaAccessChecked {
level: AccessLevel,
rule_was_trap: bool,
_seal: AccessSeal,
}
#[derive(Debug, Clone, Copy)]
struct AccessSeal;
#[derive(Debug, Clone, Copy)]
pub struct ReadGranted {
_seal: AccessSeal,
}
#[derive(Debug, Clone, Copy)]
pub struct WriteGranted {
rule_was_trap: bool,
_seal: AccessSeal,
}
impl WriteGranted {
pub fn rule_was_trap(&self) -> bool {
self.rule_was_trap
}
}
#[derive(Debug, Clone, Copy)]
pub enum AccessDenied {
NoRead,
NoWrite,
}
impl AccessDenied {
pub fn eca_code(&self) -> u32 {
match self {
AccessDenied::NoRead => ECA_NORDACCESS,
AccessDenied::NoWrite => ECA_NOWTACCESS,
}
}
}
impl CaAccessChecked {
pub(crate) fn from_level(level: AccessLevel, rule_was_trap: bool) -> Self {
Self {
level,
rule_was_trap,
_seal: AccessSeal,
}
}
pub(crate) fn denied() -> Self {
Self::from_level(AccessLevel::NoAccess, false)
}
pub fn require_read(&self) -> Result<ReadGranted, AccessDenied> {
if matches!(self.level, AccessLevel::NoAccess) {
Err(AccessDenied::NoRead)
} else {
Ok(ReadGranted { _seal: AccessSeal })
}
}
pub fn require_write(&self) -> Result<WriteGranted, AccessDenied> {
if matches!(self.level, AccessLevel::ReadWrite) {
Ok(WriteGranted {
rule_was_trap: self.rule_was_trap,
_seal: AccessSeal,
})
} else {
Err(AccessDenied::NoWrite)
}
}
pub fn level(&self) -> AccessLevel {
self.level
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn no_access_denies_read_and_write() {
let c = CaAccessChecked::from_level(AccessLevel::NoAccess, false);
assert!(matches!(c.require_read(), Err(AccessDenied::NoRead)));
assert!(matches!(c.require_write(), Err(AccessDenied::NoWrite)));
}
#[test]
fn read_grants_read_denies_write() {
let c = CaAccessChecked::from_level(AccessLevel::Read, false);
assert!(c.require_read().is_ok());
assert!(matches!(c.require_write(), Err(AccessDenied::NoWrite)));
}
#[test]
fn read_write_grants_both() {
let c = CaAccessChecked::from_level(AccessLevel::ReadWrite, false);
assert!(c.require_read().is_ok());
assert!(c.require_write().is_ok());
}
#[test]
fn mr_r20_write_granted_carries_trap_mask() {
let trapped = CaAccessChecked::from_level(AccessLevel::ReadWrite, true);
assert!(trapped.require_write().unwrap().rule_was_trap());
let untrapped = CaAccessChecked::from_level(AccessLevel::ReadWrite, false);
assert!(!untrapped.require_write().unwrap().rule_was_trap());
}
#[test]
fn denied_helper_returns_no_access_token() {
let c = CaAccessChecked::denied();
assert!(matches!(c.require_read(), Err(AccessDenied::NoRead)));
assert!(matches!(c.require_write(), Err(AccessDenied::NoWrite)));
}
#[test]
fn eca_codes_match_wire_constants() {
assert_eq!(AccessDenied::NoRead.eca_code(), ECA_NORDACCESS);
assert_eq!(AccessDenied::NoWrite.eca_code(), ECA_NOWTACCESS);
}
}