use crate::{Dkim2Result, Error, dkim2::Dkim2Error};
#[derive(Debug, PartialEq, Eq)]
pub(crate) enum Failure {
TempError(Dkim2Error),
Fail(Dkim2Error),
PermError(Dkim2Error),
}
impl Failure {
pub(crate) fn into_error(self) -> Dkim2Error {
match self {
Failure::TempError(error) | Failure::Fail(error) | Failure::PermError(error) => error,
}
}
}
impl From<Failure> for Dkim2Result {
fn from(failure: Failure) -> Self {
match failure {
Failure::TempError(error) => Dkim2Result::TempError(Error::Dkim2(error)),
Failure::Fail(error) => Dkim2Result::Fail(Error::Dkim2(error)),
Failure::PermError(error) => Dkim2Result::PermError(Error::Dkim2(error)),
}
}
}
#[derive(Default)]
pub(crate) struct Tally {
members: u32,
passed: bool,
temp_error: Option<Dkim2Error>,
fail: Option<Dkim2Error>,
perm_error: Option<Dkim2Error>,
}
impl Tally {
pub(crate) fn record(&mut self, outcome: Result<(), Failure>) {
self.members = self.members.saturating_add(1);
match outcome {
Ok(()) => self.passed = true,
Err(Failure::TempError(error)) => {
self.temp_error.get_or_insert(error);
}
Err(Failure::Fail(error)) => {
self.fail.get_or_insert(error);
}
Err(Failure::PermError(error)) => {
self.perm_error.get_or_insert(error);
}
}
}
pub(crate) fn passed(&self) -> bool {
self.passed
}
pub(crate) fn failed(&self) -> bool {
self.fail.is_some()
}
pub(crate) fn all(self) -> Result<(), Failure> {
match (self.temp_error, self.fail, self.perm_error) {
(None, None, None) => Ok(()),
(Some(error), None, None) => Err(Failure::TempError(error)),
(_, Some(error), _) => Err(Failure::Fail(error)),
(_, None, Some(error)) => Err(Failure::PermError(error)),
}
}
pub(crate) fn any(self, no_group_passed: Dkim2Error) -> Result<(), Failure> {
if self.passed {
Ok(())
} else if let Some(error) = self.temp_error {
Err(Failure::TempError(error))
} else if let Some(error) = self.fail {
Err(Failure::Fail(if self.members > 1 {
no_group_passed
} else {
error
}))
} else {
Err(Failure::PermError(
self.perm_error.unwrap_or(no_group_passed),
))
}
}
}
#[cfg(test)]
mod test {
use super::*;
fn temp() -> Result<(), Failure> {
Err(Failure::TempError(Dkim2Error::SequenceGap(1)))
}
fn fail() -> Result<(), Failure> {
Err(Failure::Fail(Dkim2Error::SequenceGap(2)))
}
fn perm() -> Result<(), Failure> {
Err(Failure::PermError(Dkim2Error::SequenceGap(3)))
}
fn tally(outcomes: impl IntoIterator<Item = Result<(), Failure>>) -> Tally {
let mut tally = Tally::default();
for outcome in outcomes {
tally.record(outcome);
}
tally
}
#[test]
fn all_rules() {
assert_eq!(tally([Ok(()), Ok(())]).all(), Ok(()));
assert_eq!(tally([Ok(()), temp()]).all(), temp());
assert_eq!(tally([temp(), fail(), perm()]).all(), fail());
assert_eq!(tally([temp(), perm()]).all(), perm());
assert_eq!(tally([Ok(()), perm()]).all(), perm());
}
#[test]
fn any_rules() {
let none = || Dkim2Error::NoSignatureGroupPassed(9);
assert_eq!(tally([perm(), Ok(())]).any(none()), Ok(()));
assert_eq!(tally([fail(), temp(), perm()]).any(none()), temp());
assert_eq!(
tally([perm(), fail()]).any(none()),
Err(Failure::Fail(none()))
);
assert_eq!(tally([fail()]).any(none()), fail());
assert_eq!(tally([perm(), perm()]).any(none()), perm());
assert_eq!(
Tally::default().any(none()),
Err(Failure::PermError(none()))
);
}
}