use serde::{Serialize, Deserialize};
use strum::{
EnumDiscriminants,
EnumString
};
use tokio::process::Command;
use crate::message::{
DynamicPayload,
DynamicPayloadType,
PingOrPong,
Packet
};
use crate::peer::{
Address,
Clock,
NodeID,
status::Status
};
pub trait Event {
type Filter: Filter<Self>;
const NAME: &'static str;
fn into_envs(self, cmd: &mut Command);
}
pub trait Filter<E: Event + ?Sized> {
fn matches(&self, evt: &E) -> bool;
}
#[derive(Clone, Debug, EnumDiscriminants)]
#[strum_discriminants(name(EventType), derive(Serialize, Deserialize))]
pub enum AnyEvent {
Dynamic(DynamicEvent),
Peer(PeerEvent),
Daemon(DaemonEvent)
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct DynamicEvent {
pub src: NodeID,
pub pyl: DynamicPayload
}
#[derive(Clone, Debug)]
pub struct PeerEvent {
pub id: NodeID,
pub evt: PeerEventContent
}
#[derive(Clone, Debug, EnumString, PartialEq, Eq)]
#[strum(serialize_all = "snake_case")]
pub enum DaemonEvent {
Start,
Stop,
Active,
Inactive
}
#[derive(Clone, Debug)]
pub enum PeerEventContent {
Seen {
clock: Clock,
png: PingOrPong,
},
NewAddress(Address),
StatusChanged(Status)
}
pub type DaemonEventFilter = Option<DaemonEvent>;
pub type TagFilter = Option<String>;
pub type NodeFilter = Option<NodeID>;
#[derive(Debug)]
pub struct DynamicEventFilter {
pub from: NodeFilter,
pub with: TagFilter,
pub typ: DynamicPayloadFilter
}
#[derive(Debug)]
pub struct PeerEventFilter {
pub from: NodeFilter,
pub typ: PeerEventTypeFilter
}
#[derive(Copy, Clone, Debug)]
pub enum DynamicPayloadFilter {
Any,
Push,
Query,
AnyResponse,
Response(Clock)
}
#[derive(Copy, Clone, Debug)]
pub enum PeerEventTypeFilter {
Any,
Seen,
NewAddress,
StatusChanged
}
impl PeerEvent {
pub fn seen(pkt: &Packet) -> Self {
Self {
id: pkt.src,
evt: PeerEventContent::Seen {
clock: pkt.clk,
png: pkt.png
}
}
}
pub fn new_address(id: NodeID, address: Address) -> Self {
let evt = PeerEventContent::NewAddress(address);
Self {id, evt}
}
pub fn changed(id: NodeID, status: Status) -> Self {
let evt = PeerEventContent::StatusChanged(status);
Self {id, evt}
}
}
impl Event for DynamicEvent {
type Filter = DynamicEventFilter;
const NAME: &'static str = "dynamic";
fn into_envs(self, cmd: &mut Command) {
let typ: DynamicPayloadType = (&self.pyl).into();
let (DynamicPayload::Push{tag, msg} |
DynamicPayload::Query{tag, msg} |
DynamicPayload::Response{tag, msg, ..}) = self.pyl;
cmd.envs([
("CNSPRCY_EVENT_SRC", self.src.to_string()),
("CNSPRCY_EVENT_DYN", format!("{typ:?}")),
("CNSPRCY_EVENT_TAG", tag),
("CNSPRCY_EVENT_MSG", msg)
]);
}
}
impl From<(NodeID, DynamicPayload)> for DynamicEvent {
fn from((src, pyl): (NodeID, DynamicPayload)) -> Self {Self{src, pyl}}
}
impl Event for PeerEvent {
type Filter = PeerEventFilter;
const NAME: &'static str = "peer";
fn into_envs(self, cmd: &mut Command) {
cmd.envs([
("CNSPRCY_PEER_ID", self.id.to_string()),
("CNSPRCY_PEER_UPDATE", format!("{:?}", self.evt))
]);
}
}
impl Event for DaemonEvent {
type Filter = DaemonEventFilter;
const NAME: &'static str = "daemon";
fn into_envs(self, cmd: &mut Command) {
cmd.env("CNSPRCY_DAEMON", format!("{self:?}"));
}
}
impl From<DynamicEvent> for AnyEvent {
fn from(event: DynamicEvent) -> Self {Self::Dynamic(event)}
}
impl From<(NodeID, DynamicPayload)> for AnyEvent {
fn from((src, evt): (NodeID, DynamicPayload)) -> Self {
Self::Dynamic(DynamicEvent{src, pyl: evt})
}
}
impl From<PeerEvent> for AnyEvent {
fn from(event: PeerEvent) -> Self {Self::Peer(event)}
}
impl From<DaemonEvent> for AnyEvent {
fn from(event: DaemonEvent) -> Self {Self::Daemon(event)}
}
impl<E: Event, F: Filter<E>> Filter<E> for Option<F> {
fn matches(&self, event: &E) -> bool {
self
.as_ref()
.map(|filter| filter.matches(event))
.unwrap_or(true)
}
}
impl Filter<DaemonEvent> for DaemonEventFilter {
fn matches(&self, event: &DaemonEvent) -> bool {
self
.as_ref()
.map(|evt| evt == event)
.unwrap_or(true)
}
}
impl Filter<DynamicEvent> for DynamicEventFilter {
fn matches(&self, event: &DynamicEvent) -> bool {
self.from
.map(|id| id == event.src)
.unwrap_or(true)
&& self.with
.as_ref()
.map(|tag| tag == event.pyl.borrow_tag())
.unwrap_or(true)
&& self.typ
.matches(&event.pyl)
}
}
impl DynamicPayloadFilter {
pub fn matches(&self, dyn_payload: &DynamicPayload) -> bool {
use DynamicPayload::*;
match (self, dyn_payload) {
(Self::Any, _) => true,
(Self::Push, Push{..}) => true,
(Self::Query, Query{..}) => true,
(Self::AnyResponse, Response{..}) => true,
(Self::Response(a), Response{to: (_, b), ..}) => a == b,
_ => false
}
}
}
impl Filter<PeerEvent> for PeerEventFilter {
fn matches(&self, event: &PeerEvent) -> bool {
self.from
.map(|id| id == event.id)
.unwrap_or(true)
&& self.typ
.matches(&event.evt)
}
}
impl PeerEventTypeFilter {
pub fn matches(&self, peer_event: &PeerEventContent) -> bool {
use PeerEventContent::*;
matches!((self, peer_event),
(Self::Any, _) |
(Self::Seen, Seen{..}) |
(Self::NewAddress, NewAddress(_)) |
(Self::StatusChanged, StatusChanged(_))
)
}
}
impl DynamicEventFilter {
pub fn simple(from: NodeFilter, with: TagFilter) -> Self {
Self {
from,
with,
typ: DynamicPayloadFilter::Any
}
}
}
impl PeerEventFilter {
pub fn new(from: NodeFilter, typ: PeerEventTypeFilter) -> Self {
Self {from, typ}
}
}