Skip to main content

key_vault/monitor/
composite.rs

1//! [`CompositeMonitor`] — fan-out across several `SecurityMonitor` impls.
2
3use alloc::sync::Arc;
4use alloc::vec::Vec;
5use core::fmt;
6
7use super::{AccessContext, FailureContext, SecurityMonitor, ThresholdContext};
8
9/// `SecurityMonitor` that fans every event out to a list of inner
10/// monitors.
11///
12/// Useful when you want, for example, a `LogMonitor` for human-readable
13/// alerts and a custom `MetricsMonitor` for dashboards from the same
14/// vault. Inner monitors are called in registration order; one failing
15/// monitor does not affect the others (monitor implementations should
16/// not panic per the trait contract).
17///
18/// # Examples
19///
20/// ```
21/// use std::sync::Arc;
22/// use key_vault::{CompositeMonitor, NoMonitor, SecurityMonitor};
23///
24/// let composite = CompositeMonitor::new(vec![
25///     Arc::new(NoMonitor) as Arc<dyn SecurityMonitor>,
26///     Arc::new(NoMonitor) as Arc<dyn SecurityMonitor>,
27/// ]);
28/// assert_eq!(composite.len(), 2);
29/// ```
30#[derive(Clone)]
31pub struct CompositeMonitor {
32    inner: Vec<Arc<dyn SecurityMonitor>>,
33}
34
35impl CompositeMonitor {
36    /// Construct a composite over the supplied inner monitors. An empty
37    /// list is permitted and yields a no-op monitor (effectively the same
38    /// as [`NoMonitor`](super::NoMonitor)).
39    #[must_use]
40    pub fn new(inner: Vec<Arc<dyn SecurityMonitor>>) -> Self {
41        Self { inner }
42    }
43
44    /// Number of inner monitors.
45    #[must_use]
46    pub fn len(&self) -> usize {
47        self.inner.len()
48    }
49
50    /// `true` if no inner monitors are registered.
51    #[must_use]
52    pub fn is_empty(&self) -> bool {
53        self.inner.is_empty()
54    }
55}
56
57impl fmt::Debug for CompositeMonitor {
58    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59        f.debug_struct("CompositeMonitor")
60            .field("inner_count", &self.inner.len())
61            .finish()
62    }
63}
64
65impl SecurityMonitor for CompositeMonitor {
66    fn on_decryption_failure(&self, ctx: &FailureContext) {
67        for m in &self.inner {
68            m.on_decryption_failure(ctx);
69        }
70    }
71
72    fn on_anomalous_access(&self, ctx: &AccessContext) {
73        for m in &self.inner {
74            m.on_anomalous_access(ctx);
75        }
76    }
77
78    fn on_threshold_breach(&self, ctx: &ThresholdContext) {
79        for m in &self.inner {
80            m.on_threshold_breach(ctx);
81        }
82    }
83}