sinks.rs
98 lignes · 2768 octets
//! Built-in [`GuardEventSink`] implementations. //! //! Two sinks are provided out of the box: //! //! - [`TracingSink`] — emits each event via `tracing::warn!` under //! the `rustwarden_fediverse_guard` target, picked up by journald, //! `tracing_subscriber` console layers, or OpenTelemetry exporters. //! - [`MultiSink`] — fan-out to several sinks sequentially. A slow //! sink does **not** back-pressure the federation hot path because //! `emit` is fire-and-forget on each. use std::sync::Arc; use async_trait::async_trait; use crate::events::{GuardEvent, GuardEventSink}; /// Sink that emits each event via `tracing::warn!`. #[derive(Debug, Default, Clone, Copy)] pub struct TracingSink; impl TracingSink { /// Build a new tracing sink. #[must_use] pub const fn new() -> Self { Self } } #[async_trait] impl GuardEventSink for TracingSink { async fn emit(&self, event: &GuardEvent) { tracing::warn!( target: "rustwarden_fediverse_guard", event_kind = ?event.kind, severity = ?event.severity, domain = %event.domain, actor_uri = event.actor_uri.as_deref().unwrap_or(""), details = %event.details, "fediverse guard event", ); } } /// Fan-out sink that calls every child in sequence. pub struct MultiSink { children: Vec<Arc<dyn GuardEventSink>>, } impl MultiSink { /// Build a new multi-sink from a vector of children. #[must_use] pub fn new(children: Vec<Arc<dyn GuardEventSink>>) -> Self { Self { children } } } #[async_trait] impl GuardEventSink for MultiSink { async fn emit(&self, event: &GuardEvent) { for child in &self.children { child.emit(event).await; } } } #[cfg(test)] mod tests { use super::*; use crate::events::{GuardEventKind, Severity}; use std::sync::Mutex; struct Recorder { events: Mutex<Vec<GuardEvent>>, } #[async_trait] impl GuardEventSink for Recorder { async fn emit(&self, event: &GuardEvent) { self.events.lock().unwrap().push(event.clone()); } } #[tokio::test] async fn multi_sink_dispatches_to_every_child() { let r1 = Arc::new(Recorder { events: Mutex::new(Vec::new()), }); let r2 = Arc::new(Recorder { events: Mutex::new(Vec::new()), }); let multi = MultiSink::new(vec![r1.clone() as Arc<dyn GuardEventSink>, r2.clone()]); let evt = GuardEvent::new(GuardEventKind::SignatureFailure, "x.tld", Severity::Medium); multi.emit(&evt).await; assert_eq!(r1.events.lock().unwrap().len(), 1); assert_eq!(r2.events.lock().unwrap().len(), 1); } }
GitRust