use std::sync::Arc;
use serde::de::DeserializeOwned;
use crate::{
any_filter::AnyFilter,
filter::{FilterError, HttpFilter},
tcp_filter::TcpFilter,
};
pub fn parse_filter_config<T: DeserializeOwned>(name: &str, config: &serde_yaml::Value) -> Result<T, FilterError> {
serde_yaml::from_value(config.clone()).map_err(|e| -> FilterError { format!("{name}: {e}").into() })
}
pub type HttpFilterFactory = Arc<dyn Fn(&serde_yaml::Value) -> Result<Box<dyn HttpFilter>, FilterError> + Send + Sync>;
pub type TcpFilterFactory = Arc<dyn Fn(&serde_yaml::Value) -> Result<Box<dyn TcpFilter>, FilterError> + Send + Sync>;
pub enum FilterFactory {
Http(HttpFilterFactory),
Tcp(TcpFilterFactory),
}
impl FilterFactory {
pub(crate) fn create(&self, config: &serde_yaml::Value) -> Result<AnyFilter, FilterError> {
match self {
Self::Http(f) => Ok(AnyFilter::Http(f(config)?)),
Self::Tcp(f) => Ok(AnyFilter::Tcp(f(config)?)),
}
}
}
#[allow(clippy::type_complexity, reason = "complex function pointer")]
pub fn http_builtin(f: fn(&serde_yaml::Value) -> Result<Box<dyn HttpFilter>, FilterError>) -> FilterFactory {
FilterFactory::Http(Arc::new(f))
}
#[allow(clippy::type_complexity, reason = "complex function pointer")]
pub fn tcp_builtin(f: fn(&serde_yaml::Value) -> Result<Box<dyn TcpFilter>, FilterError>) -> FilterFactory {
FilterFactory::Tcp(Arc::new(f))
}
#[cfg(test)]
mod tests {
use async_trait::async_trait;
use super::*;
use crate::{actions::FilterAction, context::HttpFilterContext};
#[test]
fn http_builtin_creates_http_variant() {
fn make(_: &serde_yaml::Value) -> Result<Box<dyn HttpFilter>, FilterError> {
Ok(Box::new(MinimalFilter))
}
let factory = http_builtin(make);
let filter = factory.create(&serde_yaml::Value::Null).unwrap();
assert_eq!(filter.name(), "minimal");
assert!(matches!(filter, AnyFilter::Http(_)));
}
#[test]
fn tcp_builtin_creates_tcp_variant() {
fn make(_: &serde_yaml::Value) -> Result<Box<dyn TcpFilter>, FilterError> {
Ok(Box::new(MinimalTcpFilter))
}
let factory = tcp_builtin(make);
let filter = factory.create(&serde_yaml::Value::Null).unwrap();
assert_eq!(filter.name(), "minimal_tcp");
assert!(matches!(filter, AnyFilter::Tcp(_)));
}
struct MinimalFilter;
#[async_trait]
impl HttpFilter for MinimalFilter {
fn name(&self) -> &'static str {
"minimal"
}
async fn on_request(&self, _ctx: &mut HttpFilterContext<'_>) -> Result<FilterAction, FilterError> {
Ok(FilterAction::Continue)
}
}
struct MinimalTcpFilter;
#[async_trait]
impl TcpFilter for MinimalTcpFilter {
fn name(&self) -> &'static str {
"minimal_tcp"
}
}
}