pub trait Specification<T: ?Sized>: Sized {
type Error;
fn is_satisfied_by(&self, candidate: &T) -> Result<bool, Self::Error>;
fn and<S>(self, other: S) -> And<Self, S>
where
S: Specification<T, Error = Self::Error>,
{
And {
left: self,
right: other,
}
}
fn or<S>(self, other: S) -> Or<Self, S>
where
S: Specification<T, Error = Self::Error>,
{
Or {
left: self,
right: other,
}
}
fn not(self) -> Not<Self> {
Not { inner: self }
}
}
pub struct And<L, R> {
left: L,
right: R,
}
impl<L, R> And<L, R> {
pub fn new(left: L, right: R) -> Self {
Self { left, right }
}
}
impl<T: ?Sized, L, R> Specification<T> for And<L, R>
where
L: Specification<T>,
R: Specification<T, Error = L::Error>,
{
type Error = L::Error;
fn is_satisfied_by(&self, candidate: &T) -> Result<bool, Self::Error> {
if !self.left.is_satisfied_by(candidate)? {
return Ok(false);
}
self.right.is_satisfied_by(candidate)
}
}
pub struct Or<L, R> {
left: L,
right: R,
}
impl<L, R> Or<L, R> {
pub fn new(left: L, right: R) -> Self {
Self { left, right }
}
}
impl<T: ?Sized, L, R> Specification<T> for Or<L, R>
where
L: Specification<T>,
R: Specification<T, Error = L::Error>,
{
type Error = L::Error;
fn is_satisfied_by(&self, candidate: &T) -> Result<bool, Self::Error> {
if self.left.is_satisfied_by(candidate)? {
return Ok(true);
}
self.right.is_satisfied_by(candidate)
}
}
pub struct Not<S> {
inner: S,
}
impl<S> Not<S> {
pub fn new(inner: S) -> Self {
Self { inner }
}
}
impl<T: ?Sized, S> Specification<T> for Not<S>
where
S: Specification<T>,
{
type Error = S::Error;
fn is_satisfied_by(&self, candidate: &T) -> Result<bool, Self::Error> {
self.inner.is_satisfied_by(candidate).map(|value| !value)
}
}