use std::sync::Arc;
use serde::Serialize;
use tokio::sync::broadcast;
use crate::db::audit::AuditEntry;
use crate::db::emails::EmailSummary;
use crate::db::settings::SettingsSection;
pub trait EventSink: Send + Sync + 'static {
fn emit(&self, event: CoreEvent);
}
#[derive(Debug, Clone, Serialize)]
#[cfg_attr(feature = "specta", derive(specta::Type))]
#[serde(tag = "kind", rename_all = "camelCase", rename_all_fields = "camelCase")]
pub enum CoreEvent {
NewEmail {
mailbox_id: String,
email: EmailSummary,
},
MailboxStateChanged {
mailbox_id: String,
change: MailboxStateChange,
},
ServerStatusChanged {
status: ServerStatus,
},
SettingsChanged {
section: SettingsSection,
},
AuditAppended {
entry: AuditEntry,
},
}
#[derive(Debug, Clone, Serialize)]
#[cfg_attr(feature = "specta", derive(specta::Type))]
#[serde(tag = "kind", rename_all = "camelCase", rename_all_fields = "camelCase")]
pub enum MailboxStateChange {
Created,
Updated,
Deleted,
Started,
Stopped,
Expired,
Failed { error: String },
}
#[derive(Debug, Clone, Serialize)]
#[cfg_attr(feature = "specta", derive(specta::Type))]
#[serde(rename_all = "camelCase")]
pub struct ServerStatus {
pub running_mailboxes: u32,
pub http_running: bool,
pub errors: Vec<String>,
}
#[derive(Debug, Clone, Copy, Serialize)]
#[cfg_attr(feature = "specta", derive(specta::Type))]
#[serde(rename_all = "lowercase")]
pub enum BounceKind {
Hard,
Soft,
}
impl BounceKind {
pub fn as_str(self) -> &'static str {
match self {
BounceKind::Hard => "hard",
BounceKind::Soft => "soft",
}
}
pub fn from_str(s: &str) -> Self {
if s.eq_ignore_ascii_case("soft") {
BounceKind::Soft
} else {
BounceKind::Hard
}
}
}
impl<'de> serde::Deserialize<'de> for BounceKind {
fn deserialize<D>(de: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(de)?;
Ok(Self::from_str(&s))
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct LogSink;
impl EventSink for LogSink {
fn emit(&self, event: CoreEvent) {
tracing::info!(target: "postcrate::event", event = ?event);
}
}
#[derive(Debug, Clone)]
pub struct ChannelSink {
tx: broadcast::Sender<CoreEvent>,
}
impl ChannelSink {
pub fn new(capacity: usize) -> Self {
Self {
tx: broadcast::channel(capacity).0,
}
}
pub fn subscribe(&self) -> broadcast::Receiver<CoreEvent> {
self.tx.subscribe()
}
}
impl EventSink for ChannelSink {
fn emit(&self, event: CoreEvent) {
let _ = self.tx.send(event);
}
}
#[derive(Clone)]
pub struct ComposedSink {
sinks: Vec<Arc<dyn EventSink>>,
}
impl std::fmt::Debug for ComposedSink {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ComposedSink")
.field("len", &self.sinks.len())
.finish()
}
}
impl ComposedSink {
pub fn new(sinks: Vec<Arc<dyn EventSink>>) -> Self {
Self { sinks }
}
}
impl EventSink for ComposedSink {
fn emit(&self, event: CoreEvent) {
for s in &self.sinks {
s.emit(event.clone());
}
}
}