use std::hash::Hash;
use smallvec::SmallVec;
use crate::Timestamp;
pub trait SequencePattern: Send + 'static {
type Key: Hash + Eq + Clone + Send + 'static;
type Event;
type Anomaly;
fn observe(
&mut self,
key: &Self::Key,
event: &Self::Event,
now: Timestamp,
) -> SmallVec<[Self::Anomaly; 1]>;
fn on_tick(&mut self, now: Timestamp) -> SmallVec<[Self::Anomaly; 4]>;
}
pub trait KeylessSequencePattern: Send + 'static {
type Event;
type Anomaly;
fn observe(&mut self, event: &Self::Event, now: Timestamp) -> SmallVec<[Self::Anomaly; 1]>;
fn on_tick(&mut self, now: Timestamp) -> SmallVec<[Self::Anomaly; 4]>;
}
impl<T: KeylessSequencePattern> SequencePattern for T {
type Key = ();
type Event = T::Event;
type Anomaly = T::Anomaly;
fn observe(
&mut self,
_key: &(),
event: &T::Event,
now: Timestamp,
) -> SmallVec<[Self::Anomaly; 1]> {
KeylessSequencePattern::observe(self, event, now)
}
fn on_tick(&mut self, now: Timestamp) -> SmallVec<[Self::Anomaly; 4]> {
KeylessSequencePattern::on_tick(self, now)
}
}
#[cfg(test)]
mod tests {
use super::*;
struct AuthFlip {
last_failure: Option<Timestamp>,
}
#[derive(Debug, Clone, PartialEq)]
enum AuthEvent {
Fail,
Succeed,
}
#[derive(Debug, Clone, PartialEq)]
struct SuspiciousFlip(Timestamp);
impl KeylessSequencePattern for AuthFlip {
type Event = AuthEvent;
type Anomaly = SuspiciousFlip;
fn observe(&mut self, event: &AuthEvent, now: Timestamp) -> SmallVec<[SuspiciousFlip; 1]> {
match event {
AuthEvent::Fail => {
self.last_failure = Some(now);
SmallVec::new()
}
AuthEvent::Succeed => {
if let Some(_prev) = self.last_failure.take() {
let mut v = SmallVec::new();
v.push(SuspiciousFlip(now));
v
} else {
SmallVec::new()
}
}
}
}
fn on_tick(&mut self, _now: Timestamp) -> SmallVec<[SuspiciousFlip; 4]> {
SmallVec::new()
}
}
#[test]
fn keyless_blanket_impl() {
let mut p = AuthFlip { last_failure: None };
let v: SmallVec<[SuspiciousFlip; 1]> =
SequencePattern::observe(&mut p, &(), &AuthEvent::Fail, Timestamp::new(0, 0));
assert!(v.is_empty());
let v: SmallVec<[SuspiciousFlip; 1]> =
SequencePattern::observe(&mut p, &(), &AuthEvent::Succeed, Timestamp::new(1, 0));
assert_eq!(v.len(), 1);
assert_eq!(v[0], SuspiciousFlip(Timestamp::new(1, 0)));
}
#[test]
fn no_flip_when_no_prior_failure() {
let mut p = AuthFlip { last_failure: None };
let v = KeylessSequencePattern::observe(&mut p, &AuthEvent::Succeed, Timestamp::new(0, 0));
assert!(v.is_empty());
}
}