dstar_gateway_core/validator/
sink.rs1use super::diagnostic::Diagnostic;
4
5pub trait DiagnosticSink {
11 fn record(&mut self, diagnostic: Diagnostic);
13}
14
15#[derive(Debug, Default, Clone, Copy)]
18pub struct NullSink;
19
20impl DiagnosticSink for NullSink {
21 fn record(&mut self, _: Diagnostic) {}
22}
23
24#[derive(Debug, Default, Clone)]
27pub struct VecSink {
28 diagnostics: Vec<Diagnostic>,
29}
30
31impl VecSink {
32 #[must_use]
34 pub const fn len(&self) -> usize {
35 self.diagnostics.len()
36 }
37
38 #[must_use]
40 pub const fn is_empty(&self) -> bool {
41 self.diagnostics.is_empty()
42 }
43
44 pub fn drain(&mut self) -> impl Iterator<Item = Diagnostic> + '_ {
46 self.diagnostics.drain(..)
47 }
48}
49
50impl DiagnosticSink for VecSink {
51 fn record(&mut self, d: Diagnostic) {
52 self.diagnostics.push(d);
53 }
54}
55
56#[derive(Debug, Default, Clone, Copy)]
60pub struct TracingSink;
61
62impl DiagnosticSink for TracingSink {
63 fn record(&mut self, diagnostic: Diagnostic) {
64 tracing::warn!(?diagnostic, "dstar diagnostic recorded");
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71 use std::net::{IpAddr, Ipv4Addr, SocketAddr};
72
73 const PEER1: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 20001);
74 const PEER2: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 20002);
75
76 #[test]
77 fn null_sink_compiles_and_records_nothing() {
78 let mut sink = NullSink;
79 let d = Diagnostic::DuplicateLink1Ack { peer: PEER1 };
80 sink.record(d);
81 }
82
83 #[test]
84 fn vec_sink_collects_diagnostics() {
85 let mut sink = VecSink::default();
86 assert!(sink.is_empty());
87 sink.record(Diagnostic::DuplicateLink1Ack { peer: PEER1 });
88 sink.record(Diagnostic::DuplicateLink1Ack { peer: PEER2 });
89 assert_eq!(sink.len(), 2);
90 assert_eq!(sink.drain().count(), 2);
91 assert!(sink.is_empty());
92 }
93
94 #[test]
95 fn tracing_sink_can_be_invoked() {
96 let mut sink = TracingSink;
97 sink.record(Diagnostic::DuplicateLink1Ack { peer: PEER1 });
98 }
99}