use crate::spec::{Finding as SpecFinding, FindingLocation};
pub type Finding = Box<dyn SpecFinding>;
pub trait EnforceGate: Send + Sync {
fn id(&self) -> &'static str;
fn name(&self) -> &'static str;
fn run(&self, ctx: &crate::enforce::EnforceCtx<'_>) -> Vec<Finding>;
}
pub trait RegisteredEnforcer: Send + Sync {
fn id(&self) -> &'static str;
fn name(&self) -> &'static str;
fn run_registered(&self, ctx: &crate::enforce::EnforceCtx<'_>) -> Vec<Finding>;
}
impl<T> RegisteredEnforcer for T
where
T: EnforceGate,
{
fn id(&self) -> &'static str {
EnforceGate::id(self)
}
fn name(&self) -> &'static str {
EnforceGate::name(self)
}
fn run_registered(&self, ctx: &crate::enforce::EnforceCtx<'_>) -> Vec<Finding> {
EnforceGate::run(self, ctx)
}
}
#[derive(Debug)]
pub struct EnforcerFinding {
source: &'static str,
messages: Vec<String>,
}
impl EnforcerFinding {
#[inline]
pub fn new(source: &'static str, messages: Vec<String>) -> Self {
Self { source, messages }
}
}
impl SpecFinding for EnforcerFinding {
fn source(&self) -> &'static str {
self.source
}
fn message(&self) -> String {
self.messages.join("\n")
}
fn fix_hint(&self) -> String {
self.messages
.iter()
.find(|message| message.contains("Fix:"))
.cloned()
.unwrap_or_else(|| format!("Fix: repair {} enforcement findings.", self.source))
}
fn location(&self) -> Option<FindingLocation> {
None
}
}
#[inline]
pub fn aggregate_finding(source: &'static str, messages: Vec<String>) -> Finding {
Box::new(EnforcerFinding::new(source, messages))
}
#[inline]
pub fn finding_result(source: &'static str, messages: Vec<String>) -> Vec<Finding> {
if messages.is_empty() {
Vec::new()
} else {
vec![aggregate_finding(source, messages)]
}
}