use crate::plugin::{Context, ExecPlugin, Plugin};
use crate::{RegisterExecPlugin, Result};
use async_trait::async_trait;
use std::sync::Arc;
use tracing::info;
#[derive(Debug, Clone, RegisterExecPlugin)]
pub struct QuerySummaryPlugin {
metadata_key: String,
}
impl QuerySummaryPlugin {
pub fn new(metadata_key: impl Into<String>) -> Self {
Self {
metadata_key: metadata_key.into(),
}
}
pub fn quick_setup(s: &str) -> Self {
Self::new(s)
}
}
#[async_trait]
impl Plugin for QuerySummaryPlugin {
fn name(&self) -> &str {
"query_summary"
}
async fn execute(&self, ctx: &mut Context) -> Result<()> {
let summary: Vec<String> = ctx
.request()
.questions()
.iter()
.map(|q| format!("{} {} {}", q.qname(), q.qclass(), q.qtype()))
.collect();
let joined = summary.join("; ");
ctx.set_metadata(self.metadata_key.clone(), joined.clone());
info!(key = %self.metadata_key, summary = %joined, "query summary");
Ok(())
}
}
impl ExecPlugin for QuerySummaryPlugin {
fn quick_setup(prefix: &str, exec_str: &str) -> Result<Arc<dyn Plugin>> {
if prefix != "query_summary" {
return Err(crate::Error::Config(format!(
"ExecPlugin quick_setup: unsupported prefix '{}', expected 'query_summary'",
prefix
)));
}
let metadata_key = exec_str.trim();
if metadata_key.is_empty() {
return Err(crate::Error::Config(
"query_summary requires a metadata key (e.g., 'query_summary summary')".to_string(),
));
}
let plugin = QuerySummaryPlugin::new(metadata_key);
Ok(Arc::new(plugin))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dns::types::{RecordClass, RecordType};
use crate::dns::{Message, Question};
#[tokio::test]
async fn test_query_summary_basic() {
let plugin = QuerySummaryPlugin::new("summary");
let mut req = Message::new();
req.add_question(Question::new(
"example.com".to_string(),
RecordType::A,
RecordClass::IN,
));
let mut ctx = crate::plugin::Context::new(req);
plugin.execute(&mut ctx).await.unwrap();
let s = ctx
.get_metadata::<String>("summary")
.expect("summary metadata");
assert!(s.contains("example.com"));
}
#[tokio::test]
async fn test_query_summary_sets_metadata() {
let mut request = Message::new();
request.add_question(Question::new(
"example.com".to_string(),
RecordType::A,
RecordClass::IN,
));
request.add_question(Question::new(
"example.com".to_string(),
RecordType::AAAA,
RecordClass::IN,
));
let mut ctx = Context::new(request);
let plugin = QuerySummaryPlugin::new("summary");
plugin.execute(&mut ctx).await.unwrap();
let summary = ctx.get_metadata::<String>("summary").unwrap();
assert!(summary.contains("example.com"));
assert!(summary.contains("A"));
assert!(summary.contains("AAAA"));
}
}