use std::sync::OnceLock;
use wafrift_plugin_api::{Tamper, TamperRegistry, default_plugin_dir};
static PLUGIN_REGISTRY: OnceLock<TamperRegistry> = OnceLock::new();
fn registry() -> &'static TamperRegistry {
PLUGIN_REGISTRY.get_or_init(|| {
let mut reg = TamperRegistry::new();
if let Some(dir) = default_plugin_dir() {
let errors = reg.load_dir(&dir);
for e in errors {
tracing::warn!("plugin-bridge: skipping plugin: {e}");
}
}
reg
})
}
#[must_use]
pub fn apply_all_plugins(payload: &str) -> Vec<(String, String)> {
registry()
.all()
.iter()
.map(|t| (t.name().to_owned(), t.apply(payload)))
.collect()
}
#[must_use]
pub fn apply_plugin(name: &str, payload: &str) -> Option<String> {
registry().get(name).map(|t: &dyn Tamper| t.apply(payload))
}
#[must_use]
pub fn plugin_names() -> Vec<String> {
registry()
.all()
.iter()
.map(|t| t.name().to_owned())
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn apply_all_with_no_plugins_returns_empty() {
let result = apply_all_plugins("SELECT 1");
for (name, _transformed) in &result {
assert!(!name.is_empty());
}
}
#[test]
fn apply_unknown_plugin_returns_none() {
let result = apply_plugin("__nonexistent_plugin_xyz__", "payload");
assert!(result.is_none());
}
#[test]
fn plugin_names_returns_vec() {
let names = plugin_names();
for n in &names {
assert!(!n.is_empty());
}
}
}