use vyre_foundation::ir::Program;
pub(crate) mod private {
pub trait Sealed {}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum EnforceVerdict {
Allow,
Deny {
policy: &'static str,
detail: String,
},
}
impl std::fmt::Display for EnforceVerdict {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Allow => f.write_str("allow"),
Self::Deny { policy, detail } => {
write!(f, "deny[{policy}]: {detail}")
}
}
}
}
pub trait EnforceGate: private::Sealed + Send + Sync {
fn name(&self) -> &'static str;
fn evaluate(&self, program: &Program) -> EnforceVerdict;
}
pub struct Chain<A, B> {
first: A,
second: B,
}
impl<A: EnforceGate, B: EnforceGate> private::Sealed for Chain<A, B> {}
impl<A: EnforceGate, B: EnforceGate> Chain<A, B> {
pub fn new(first: A, second: B) -> Self {
Self { first, second }
}
}
impl<A: EnforceGate, B: EnforceGate> EnforceGate for Chain<A, B> {
fn name(&self) -> &'static str {
"chain"
}
fn evaluate(&self, program: &Program) -> EnforceVerdict {
match self.first.evaluate(program) {
EnforceVerdict::Allow => self.second.evaluate(program),
deny => deny,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn allow_verdict_displays_as_allow() {
assert_eq!(format!("{}", EnforceVerdict::Allow), "allow");
}
#[test]
fn deny_verdict_displays_policy_and_detail() {
let verdict =
EnforceVerdict::Deny { policy: "registry_gate", detail: "Fix: do not use".into() };
assert_eq!(
format!("{}", verdict),
"deny[registry_gate]: Fix: do not use"
);
}
}