use std::sync::Arc;
use crate::{
config::ConfigType,
filters::{CreationError, FilterKind, StaticFilter},
};
pub type DynFilterFactory = Box<dyn FilterFactory>;
#[derive(Clone)]
#[non_exhaustive]
pub struct FilterInstance(Arc<FilterInstanceData>);
struct FilterInstanceData {
pub config: serde_json::Value,
pub label: Option<String>,
pub filter: FilterKind,
}
impl FilterInstance {
pub fn new(config: serde_json::Value, filter: FilterKind) -> Self {
Self(Arc::new(FilterInstanceData {
config,
label: None,
filter,
}))
}
pub fn testing(filter: impl Into<FilterKind>) -> Self {
Self(Arc::new(FilterInstanceData {
config: serde_json::Value::Null,
label: None,
filter: filter.into(),
}))
}
pub fn config(&self) -> &serde_json::Value {
&self.0.config
}
pub fn label(&self) -> Option<&str> {
self.0.label.as_deref()
}
pub fn filter(&self) -> &FilterKind {
&self.0.filter
}
}
pub trait FilterFactory: Sync + Send {
fn name(&self) -> &'static str;
fn config_schema(&self) -> schemars::Schema;
fn create_filter(&self, args: CreateFilterArgs) -> Result<FilterInstance, CreationError>;
fn encode_config_to_protobuf(
&self,
args: serde_json::Value,
) -> Result<prost_types::Any, CreationError>;
fn encode_config_to_json(
&self,
args: prost_types::Any,
) -> Result<serde_json::Value, CreationError>;
fn require_config(&self, config: Option<ConfigType>) -> Result<ConfigType, CreationError> {
config.ok_or_else(|| CreationError::MissingConfig(self.name()))
}
}
impl<F> FilterFactory for std::marker::PhantomData<fn() -> F>
where
F: StaticFilter + 'static,
CreationError: From<<F::Configuration as TryFrom<F::BinaryConfiguration>>::Error>
+ From<<F::BinaryConfiguration as TryFrom<F::Configuration>>::Error>,
{
fn name(&self) -> &'static str {
F::NAME
}
fn config_schema(&self) -> schemars::Schema {
schemars::schema_for!(F::Configuration)
}
fn create_filter(&self, args: CreateFilterArgs) -> Result<FilterInstance, CreationError> {
let (config_json, config): (_, Option<F::Configuration>) = if let Some(config) = args.config
{
config
.deserialize::<F::Configuration, F::BinaryConfiguration>(self.name())
.map(|(j, c)| (j, Some(c)))?
} else {
(serde_json::Value::Null, None)
};
Ok(FilterInstance::new(
config_json,
F::try_from_config(config)?.into(),
))
}
fn encode_config_to_protobuf(
&self,
config: serde_json::Value,
) -> Result<prost_types::Any, CreationError> {
let config: F::Configuration = serde_json::from_value(config)?;
Ok(prost_types::Any {
type_url: self.name().into(),
value: crate::codec::prost::encode::<F::BinaryConfiguration>(&config.try_into()?)?,
})
}
fn encode_config_to_json(
&self,
config: prost_types::Any,
) -> Result<serde_json::Value, CreationError> {
if self.name() != config.type_url {
return Err(crate::filters::CreationError::MismatchedTypes {
expected: self.name().into(),
actual: config.type_url,
});
}
let message = <F::BinaryConfiguration as prost::Message>::decode(&*config.value)?;
let config = F::Configuration::try_from(message)?;
Ok(serde_json::to_value(&config)?)
}
}
pub struct CreateFilterArgs {
pub config: Option<ConfigType>,
}
impl CreateFilterArgs {
pub fn new(config: Option<ConfigType>) -> CreateFilterArgs {
Self { config }
}
pub fn fixed(config: Option<serde_json::Value>) -> CreateFilterArgs {
Self::new(config.map(ConfigType::Static))
}
pub fn dynamic(config: Option<prost_types::Any>) -> CreateFilterArgs {
CreateFilterArgs::new(config.map(ConfigType::Dynamic))
}
}