use crate::push_index_if_explain;
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum EffectKind {
Allow = 0,
Indeterminate = 1,
Deny = 2,
}
pub trait Effector: Send + Sync {
fn new_stream(&self, expr: &str, cap: usize) -> Box<dyn EffectorStream>;
}
pub trait EffectorStream: Send + Sync {
fn next(&self) -> bool;
#[cfg(feature = "explain")]
fn explain(&self) -> Option<Vec<usize>>;
fn push_effect(&mut self, eft: EffectKind) -> bool;
}
#[derive(Clone)]
pub struct DefaultEffectStream {
done: bool,
res: bool,
expr: String,
idx: usize,
cap: usize,
#[cfg(feature = "explain")]
expl: Vec<usize>,
}
#[derive(Default)]
pub struct DefaultEffector;
impl Effector for DefaultEffector {
fn new_stream(&self, expr: &str, cap: usize) -> Box<dyn EffectorStream> {
assert!(cap > 0);
let res = match expr {
"some(where (p_eft == allow))"
| "some(where (p_eft == allow)) && !some(where (p_eft == deny))"
| "priority(p_eft) || deny" => false,
"!some(where (p_eft == deny))" => true,
_ => panic!("unsupported effect: `{}`", expr),
};
Box::new(DefaultEffectStream {
done: false,
res,
expr: expr.to_owned(),
cap,
idx: 0,
#[cfg(feature = "explain")]
expl: Vec::with_capacity(10),
})
}
}
impl EffectorStream for DefaultEffectStream {
#[inline]
fn next(&self) -> bool {
assert!(self.done);
self.res
}
#[cfg(feature = "explain")]
#[inline]
fn explain(&self) -> Option<Vec<usize>> {
assert!(self.done);
if self.expl.is_empty() {
None
} else {
Some(self.expl.clone())
}
}
fn push_effect(&mut self, eft: EffectKind) -> bool {
if self.expr == "some(where (p_eft == allow))" {
if eft == EffectKind::Allow {
self.done = true;
self.res = true;
push_index_if_explain!(self);
}
} else if self.expr
== "some(where (p_eft == allow)) && !some(where (p_eft == deny))"
{
if eft == EffectKind::Allow {
self.res = true;
push_index_if_explain!(self)
} else if eft == EffectKind::Deny {
self.done = true;
self.res = false;
push_index_if_explain!(self)
}
} else if self.expr == "!some(where (p_eft == deny))" {
if eft == EffectKind::Deny {
self.done = true;
self.res = false;
push_index_if_explain!(self)
}
} else if self.expr == "priority(p_eft) || deny"
&& eft != EffectKind::Indeterminate
{
self.res = eft == EffectKind::Allow;
self.done = true;
push_index_if_explain!(self)
}
if self.idx + 1 == self.cap {
self.done = true;
self.idx = self.cap;
} else {
self.idx += 1;
}
self.done
}
}