use serde::{Deserialize, Serialize};
use super::proto;
use crate::{
config::filter::Filter,
filters::{ConvertProtoConfigError, StaticFilter},
};
#[derive(Debug, Deserialize, Serialize, Eq, PartialEq, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct Config {
pub on_read: Option<DirectionalConfig>,
pub on_write: Option<DirectionalConfig>,
}
impl TryFrom<Config> for proto::Match {
type Error = crate::filters::CreationError;
fn try_from(config: Config) -> Result<Self, Self::Error> {
Ok(Self {
on_read: config.on_read.map(TryFrom::try_from).transpose()?,
on_write: config.on_write.map(TryFrom::try_from).transpose()?,
})
}
}
impl TryFrom<proto::Match> for Config {
type Error = ConvertProtoConfigError;
fn try_from(value: proto::Match) -> Result<Self, Self::Error> {
Ok(Self {
on_read: value
.on_read
.map(proto::r#match::Config::try_into)
.transpose()
.map_err(|error: eyre::Report| {
ConvertProtoConfigError::new(error, Some("on_read".into()))
})?,
on_write: value
.on_write
.map(proto::r#match::Config::try_into)
.transpose()
.map_err(|error: eyre::Report| {
ConvertProtoConfigError::new(error, Some("on_write".into()))
})?,
})
}
}
#[derive(Debug, Deserialize, Serialize, Eq, PartialEq, schemars::JsonSchema)]
pub struct DirectionalConfig {
#[serde(rename = "metadataKey")]
pub metadata_key: crate::net::endpoint::metadata::Key,
pub branches: Vec<Branch>,
#[serde(default)]
pub fallthrough: Fallthrough,
}
impl TryFrom<DirectionalConfig> for proto::r#match::Config {
type Error = crate::filters::CreationError;
fn try_from(config: DirectionalConfig) -> Result<Self, Self::Error> {
Ok(Self {
metadata_key: Some(config.metadata_key.to_string()),
branches: config
.branches
.into_iter()
.map(TryFrom::try_from)
.collect::<Result<_, _>>()?,
fallthrough: config.fallthrough.try_into().map(Some)?,
})
}
}
impl TryFrom<proto::r#match::Config> for DirectionalConfig {
type Error = eyre::Report;
fn try_from(value: proto::r#match::Config) -> Result<Self, Self::Error> {
Ok(Self {
metadata_key: value.metadata_key.map(From::from).ok_or_else(|| {
ConvertProtoConfigError::new("Missing", Some("metadata_key".into()))
})?,
branches: value
.branches
.into_iter()
.map(proto::r#match::Branch::try_into)
.collect::<Result<_, _>>()?,
fallthrough: value
.fallthrough
.ok_or_else(|| ConvertProtoConfigError::new("Missing", Some("fallthrough".into())))?
.try_into()
.map(Fallthrough)?,
})
}
}
#[derive(Debug, Deserialize, Serialize, Eq, PartialEq, schemars::JsonSchema)]
pub struct Branch {
pub value: crate::net::endpoint::metadata::Value,
#[serde(flatten)]
pub filter: Filter,
}
impl TryFrom<Branch> for proto::r#match::Branch {
type Error = crate::filters::CreationError;
fn try_from(branch: Branch) -> Result<Self, Self::Error> {
Ok(Self {
value: Some(branch.value.into()),
filter: branch.filter.try_into().map(Some)?,
})
}
}
impl TryFrom<proto::r#match::Branch> for Branch {
type Error = eyre::Report;
fn try_from(branch: proto::r#match::Branch) -> Result<Self, Self::Error> {
Ok(Self {
value: branch
.value
.ok_or_else(|| ConvertProtoConfigError::new("Missing", Some("value".into())))?
.try_into()?,
filter: branch
.filter
.map(|filter| filter.try_into())
.transpose()?
.ok_or_else(|| ConvertProtoConfigError::new("Missing", Some("filter".into())))?,
})
}
}
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(transparent)]
pub struct Fallthrough(pub Filter);
impl Default for Fallthrough {
fn default() -> Self {
Self(crate::filters::Drop::as_filter_config(None).unwrap())
}
}
impl TryFrom<Fallthrough> for crate::generated::envoy::config::listener::v3::Filter {
type Error = crate::filters::CreationError;
fn try_from(fallthrough: Fallthrough) -> Result<Self, Self::Error> {
fallthrough.0.try_into()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serde() {
let matches_yaml = "
on_read:
metadataKey: quilkin.dev/captured_bytes
branches:
- value: abc
name: quilkin.filters.debug.v1alpha1.Debug
";
let config = serde_yaml::from_str::<Config>(matches_yaml).unwrap();
assert_eq!(
config,
Config {
on_read: Some(DirectionalConfig {
metadata_key: "quilkin.dev/captured_bytes".into(),
branches: vec![Branch {
value: String::from("abc").into(),
filter: crate::filters::Debug::as_filter_config(None).unwrap(),
}],
fallthrough: <_>::default(),
}),
on_write: None,
}
);
}
}