use super::*;
use std::{collections::HashSet, sync::Mutex};
#[doc(hidden)]
#[derive(Clone)]
pub struct SelectedAuditReporter {
pub identifier: ObjectIdentifier,
pub status: Arc<AuditReporterStatus>,
pub configuration: AuditReporterConfiguration,
}
#[doc(hidden)]
pub struct TargetAuditAssociation {
reporters: Vec<(ObjectIdentifier, Arc<AuditReporterStatus>)>,
subjects: Mutex<HashSet<ObjectIdentifier>>,
}
impl TargetAuditAssociation {
pub fn new(reporters: Vec<(ObjectIdentifier, Arc<AuditReporterStatus>)>) -> Arc<Self> {
Arc::new(Self {
reporters,
subjects: Mutex::new(HashSet::new()),
})
}
pub fn reporters(&self) -> &[(ObjectIdentifier, Arc<AuditReporterStatus>)] {
&self.reporters
}
pub fn snapshots(
&self,
replacement: Option<(ObjectIdentifier, &AuditReporterConfiguration)>,
) -> Vec<SelectedAuditReporter> {
self.reporters
.iter()
.map(|(identifier, status)| SelectedAuditReporter {
identifier: *identifier,
status: Arc::clone(status),
configuration: replacement
.filter(|(oid, _)| oid == identifier)
.map_or_else(|| status.configuration(), |(_, value)| value.clone()),
})
.collect()
}
pub fn select(
&self,
target: Option<ObjectIdentifier>,
kind: ObjectType,
replacement: Option<(ObjectIdentifier, &AuditReporterConfiguration)>,
) -> Option<SelectedAuditReporter> {
self.snapshots(replacement).into_iter().find(|r| {
r.configuration.enabled()
&& target.map_or_else(
|| r.configuration.monitors_unassigned(kind),
|target| r.configuration.monitors(target),
)
})
}
pub fn select_change(
&self,
target: ObjectIdentifier,
replacement: Option<(ObjectIdentifier, &AuditReporterConfiguration)>,
) -> Option<SelectedAuditReporter> {
self.select(Some(target), target.object_type(), replacement)
.or_else(|| {
self.snapshots(replacement)
.into_iter()
.find(|r| r.identifier == target && r.configuration.enabled())
})
}
pub fn select_recipient_change(&self, device: ObjectIdentifier) -> SelectedAuditReporter {
self.select(Some(device), ObjectType::DEVICE, None)
.or_else(|| {
self.snapshots(None)
.into_iter()
.find(|r| r.configuration.enabled())
})
.unwrap_or_else(|| self.snapshots(None).remove(0))
}
pub(crate) fn set_subjects(&self, subjects: impl IntoIterator<Item = ObjectIdentifier>) {
*self.subjects.lock().unwrap() = subjects.into_iter().collect();
self.refresh_overlap();
}
pub(crate) fn membership_changed(&self, oid: ObjectIdentifier, present: bool) {
let mut subjects = self.subjects.lock().unwrap();
if present {
subjects.insert(oid);
} else {
subjects.remove(&oid);
}
drop(subjects);
self.refresh_overlap();
}
pub fn refresh_overlap(&self) {
let snapshots = self.snapshots(None);
let subjects = self.subjects.lock().unwrap();
let mut overlaps = vec![false; snapshots.len()];
for subject in subjects.iter() {
let matching: Vec<_> = snapshots
.iter()
.enumerate()
.filter(|(_, r)| r.configuration.enabled() && r.configuration.monitors(*subject))
.map(|(i, _)| i)
.collect();
if matching.len() > 1 {
for index in matching {
overlaps[index] = true;
}
}
}
for (reporter, overlap) in snapshots.iter().zip(overlaps) {
reporter.status.set_overlap(overlap);
}
}
}