use opentelemetry::Value;
use schemars::JsonSchema;
use serde::Deserialize;
use tower::BoxError;
use super::Stage;
use crate::Context;
use crate::plugins::telemetry::config::AttributeValue;
use crate::plugins::telemetry::config_new::Selector;
#[derive(Deserialize, JsonSchema, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields, rename_all = "snake_case")]
pub(crate) enum Condition<T> {
Eq([SelectorOrValue<T>; 2]),
Gt([SelectorOrValue<T>; 2]),
Lt([SelectorOrValue<T>; 2]),
Exists(T),
All(Vec<Condition<T>>),
Any(Vec<Condition<T>>),
Not(Box<Condition<T>>),
True,
False,
}
impl<T> Default for Condition<T> {
fn default() -> Self {
Self::True
}
}
impl Condition<()> {
pub(crate) fn empty<T>() -> Condition<T> {
Condition::True
}
}
#[derive(Deserialize, JsonSchema, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields, rename_all = "snake_case", untagged)]
pub(crate) enum SelectorOrValue<T> {
Value(AttributeValue),
Selector(T),
}
impl<T> Condition<T>
where
T: Selector,
{
pub(crate) fn validate(&self, restricted_stage: Option<Stage>) -> Result<(), String> {
match self {
Condition::Eq(arr) | Condition::Gt(arr) | Condition::Lt(arr) => {
match (&arr[0], &arr[1]) {
(SelectorOrValue::Value(val1), SelectorOrValue::Value(val2)) => Err(format!(
"trying to compare 2 values ('{val1}' and '{val2}'), usually it's a syntax error because you want to use a specific selector and a value in a condition"
)),
(SelectorOrValue::Value(_), SelectorOrValue::Selector(sel))
| (SelectorOrValue::Selector(sel), SelectorOrValue::Value(_)) => {
if let Some(Stage::Request) = &restricted_stage {
if !sel.is_active(Stage::Request) {
return Err(format!(
"selector {sel:?} is only valid for request stage, this log event will never trigger"
));
}
}
Ok(())
}
(SelectorOrValue::Selector(sel1), SelectorOrValue::Selector(sel2)) => {
if let Some(Stage::Request) = &restricted_stage {
if !sel1.is_active(Stage::Request) {
return Err(format!(
"selector {sel1:?} is only valid for request stage, this log event will never trigger"
));
}
if !sel2.is_active(Stage::Request) {
return Err(format!(
"selector {sel2:?} is only valid for request stage, this log event will never trigger"
));
}
}
Ok(())
}
}
}
Condition::Exists(sel) => match restricted_stage {
Some(stage) => {
if sel.is_active(stage) {
Ok(())
} else {
Err(format!(
"the 'exists' condition use a selector applied at the wrong stage, this condition will be executed at the {} stage",
stage
))
}
}
None => Ok(()),
},
Condition::All(all) => {
for cond in all {
cond.validate(restricted_stage)?;
}
Ok(())
}
Condition::Any(any) => {
for cond in any {
cond.validate(restricted_stage)?;
}
Ok(())
}
Condition::Not(cond) => cond.validate(restricted_stage),
Condition::True | Condition::False => Ok(()),
}
}
pub(crate) fn evaluate_request(&mut self, request: &T::Request) -> Option<bool> {
match self {
Condition::Eq(eq) => {
if !eq[0].is_active(Stage::Request) && !eq[1].is_active(Stage::Request) {
return None;
}
match (eq[0].on_request(request), eq[1].on_request(request)) {
(None, None) => None,
(None, Some(right)) => {
eq[1] = SelectorOrValue::Value(right.into());
None
}
(Some(left), None) => {
eq[0] = SelectorOrValue::Value(left.into());
None
}
(Some(left), Some(right)) => {
if left == right {
*self = Condition::True;
Some(true)
} else {
Some(false)
}
}
}
}
Condition::Gt(gt) => {
if !gt[0].is_active(Stage::Request) && !gt[1].is_active(Stage::Request) {
return None;
}
let left_att = gt[0].on_request(request).map(AttributeValue::from);
let right_att = gt[1].on_request(request).map(AttributeValue::from);
match (left_att, right_att) {
(None, None) => None,
(Some(l), None) => {
gt[0] = SelectorOrValue::Value(l);
None
}
(None, Some(r)) => {
gt[1] = SelectorOrValue::Value(r);
None
}
(Some(l), Some(r)) => match (l.as_f64(), r.as_f64()) {
(Some(l), Some(r)) => {
if l > r {
*self = Condition::True;
Some(true)
} else {
*self = Condition::False;
Some(false)
}
}
_ => {
if l > r {
*self = Condition::True;
Some(true)
} else {
*self = Condition::False;
Some(false)
}
}
},
}
}
Condition::Lt(lt) => {
if !lt[0].is_active(Stage::Request) && !lt[1].is_active(Stage::Request) {
return None;
}
let left_att = lt[0].on_request(request).map(AttributeValue::from);
let right_att = lt[1].on_request(request).map(AttributeValue::from);
match (left_att, right_att) {
(None, None) => None,
(Some(l), None) => {
lt[0] = SelectorOrValue::Value(l);
None
}
(None, Some(r)) => {
lt[1] = SelectorOrValue::Value(r);
None
}
(Some(l), Some(r)) => match (l.as_f64(), r.as_f64()) {
(Some(l), Some(r)) => {
if l < r {
*self = Condition::True;
Some(true)
} else {
*self = Condition::False;
Some(false)
}
}
_ => {
if l < r {
*self = Condition::True;
Some(true)
} else {
*self = Condition::False;
Some(false)
}
}
},
}
}
Condition::Exists(exist) => {
if exist.is_active(Stage::Request) {
if exist.on_request(request).is_some() {
*self = Condition::True;
Some(true)
} else {
Some(false)
}
} else {
None
}
}
Condition::All(all) => {
if all.is_empty() {
*self = Condition::True;
return Some(true);
}
let mut response = Some(true);
for cond in all {
match cond.evaluate_request(request) {
Some(resp) => {
response = response.map(|r| resp && r);
}
None => {
response = None;
}
}
}
response
}
Condition::Any(any) => {
if any.is_empty() {
*self = Condition::True;
return Some(true);
}
let mut response: Option<bool> = Some(false);
for cond in any {
match cond.evaluate_request(request) {
Some(resp) => {
response = response.map(|r| resp || r);
}
None => {
response = None;
}
}
}
response
}
Condition::Not(not) => not.evaluate_request(request).map(|r| !r),
Condition::True => Some(true),
Condition::False => Some(false),
}
}
pub(crate) fn evaluate_event_response(
&self,
response: &T::EventResponse,
ctx: &Context,
) -> bool {
match self {
Condition::Eq(eq) => {
let left = eq[0].on_response_event(response, ctx);
let right = eq[1].on_response_event(response, ctx);
left == right
}
Condition::Gt(gt) => {
let left_att = gt[0]
.on_response_event(response, ctx)
.map(AttributeValue::from);
let right_att = gt[1]
.on_response_event(response, ctx)
.map(AttributeValue::from);
left_att.zip(right_att).is_some_and(|(l, r)| l > r)
}
Condition::Lt(gt) => {
let left_att = gt[0]
.on_response_event(response, ctx)
.map(AttributeValue::from);
let right_att = gt[1]
.on_response_event(response, ctx)
.map(AttributeValue::from);
left_att.zip(right_att).is_some_and(|(l, r)| l < r)
}
Condition::Exists(exist) => exist.on_response_event(response, ctx).is_some(),
Condition::All(all) => all.iter().all(|c| c.evaluate_event_response(response, ctx)),
Condition::Any(any) => any.iter().any(|c| c.evaluate_event_response(response, ctx)),
Condition::Not(not) => !not.evaluate_event_response(response, ctx),
Condition::True => true,
Condition::False => false,
}
}
pub(crate) fn evaluate_response(&self, response: &T::Response) -> bool {
match self {
Condition::Eq(eq) => {
let left = eq[0].on_response(response);
let right = eq[1].on_response(response);
left == right
}
Condition::Gt(gt) => {
let left_att = gt[0].on_response(response).map(AttributeValue::from);
let right_att = gt[1].on_response(response).map(AttributeValue::from);
left_att.zip(right_att).is_some_and(|(l, r)| l > r)
}
Condition::Lt(gt) => {
let left_att = gt[0].on_response(response).map(AttributeValue::from);
let right_att = gt[1].on_response(response).map(AttributeValue::from);
left_att.zip(right_att).is_some_and(|(l, r)| l < r)
}
Condition::Exists(exist) => exist.on_response(response).is_some(),
Condition::All(all) => all.iter().all(|c| c.evaluate_response(response)),
Condition::Any(any) => any.iter().any(|c| c.evaluate_response(response)),
Condition::Not(not) => !not.evaluate_response(response),
Condition::True => true,
Condition::False => false,
}
}
pub(crate) fn evaluate_error(&self, error: &BoxError, ctx: &Context) -> bool {
match self {
Condition::Eq(eq) => {
let left = eq[0].on_error(error, ctx);
let right = eq[1].on_error(error, ctx);
left == right
}
Condition::Gt(gt) => {
let left_att = gt[0].on_error(error, ctx).map(AttributeValue::from);
let right_att = gt[1].on_error(error, ctx).map(AttributeValue::from);
left_att.zip(right_att).is_some_and(|(l, r)| l > r)
}
Condition::Lt(gt) => {
let left_att = gt[0].on_error(error, ctx).map(AttributeValue::from);
let right_att = gt[1].on_error(error, ctx).map(AttributeValue::from);
left_att.zip(right_att).is_some_and(|(l, r)| l < r)
}
Condition::Exists(exist) => exist.on_error(error, ctx).is_some(),
Condition::All(all) => all.iter().all(|c| c.evaluate_error(error, ctx)),
Condition::Any(any) => any.iter().any(|c| c.evaluate_error(error, ctx)),
Condition::Not(not) => !not.evaluate_error(error, ctx),
Condition::True => true,
Condition::False => false,
}
}
pub(crate) fn evaluate_response_field(
&self,
ty: &apollo_compiler::executable::NamedType,
field: &apollo_compiler::executable::Field,
value: &serde_json_bytes::Value,
ctx: &Context,
) -> bool {
match self {
Condition::Eq(eq) => {
let left = eq[0].on_response_field(ty, field, value, ctx);
let right = eq[1].on_response_field(ty, field, value, ctx);
left == right
}
Condition::Gt(gt) => {
let left_att = gt[0]
.on_response_field(ty, field, value, ctx)
.map(AttributeValue::from);
let right_att = gt[1]
.on_response_field(ty, field, value, ctx)
.map(AttributeValue::from);
left_att.zip(right_att).is_some_and(|(l, r)| l > r)
}
Condition::Lt(gt) => {
let left_att = gt[0]
.on_response_field(ty, field, value, ctx)
.map(AttributeValue::from);
let right_att = gt[1]
.on_response_field(ty, field, value, ctx)
.map(AttributeValue::from);
left_att.zip(right_att).is_some_and(|(l, r)| l < r)
}
Condition::Exists(exist) => exist.on_response_field(ty, field, value, ctx).is_some(),
Condition::All(all) => all
.iter()
.all(|c| c.evaluate_response_field(ty, field, value, ctx)),
Condition::Any(any) => any
.iter()
.any(|c| c.evaluate_response_field(ty, field, value, ctx)),
Condition::Not(not) => !not.evaluate_response_field(ty, field, value, ctx),
Condition::True => true,
Condition::False => false,
}
}
pub(crate) fn evaluate_drop(&self) -> Option<bool> {
match self {
Condition::Eq(eq) => match (eq[0].on_drop(), eq[1].on_drop()) {
(Some(left), Some(right)) => {
if left == right {
Some(true)
} else {
Some(false)
}
}
_ => None,
},
Condition::Gt(gt) => {
let left_att = gt[0].on_drop().map(AttributeValue::from);
let right_att = gt[1].on_drop().map(AttributeValue::from);
match (left_att, right_att) {
(Some(l), Some(r)) => {
if l > r {
Some(true)
} else {
Some(false)
}
}
_ => None,
}
}
Condition::Lt(lt) => {
let left_att = lt[0].on_drop().map(AttributeValue::from);
let right_att = lt[1].on_drop().map(AttributeValue::from);
match (left_att, right_att) {
(Some(l), Some(r)) => {
if l < r {
Some(true)
} else {
Some(false)
}
}
_ => None,
}
}
Condition::Exists(exist) => {
if exist.on_drop().is_some() {
Some(true)
} else {
None
}
}
Condition::All(all) => {
if all.is_empty() {
return Some(true);
}
let mut response = Some(true);
for cond in all {
match cond.evaluate_drop() {
Some(resp) => {
response = response.map(|r| resp && r);
}
None => {
response = None;
}
}
}
response
}
Condition::Any(any) => {
if any.is_empty() {
return Some(true);
}
let mut response: Option<bool> = Some(false);
for cond in any {
match cond.evaluate_drop() {
Some(resp) => {
response = response.map(|r| resp || r);
}
None => {
response = None;
}
}
}
response
}
Condition::Not(not) => not.evaluate_drop().map(|r| !r),
Condition::True => Some(true),
Condition::False => Some(false),
}
}
}
impl<T> Selector for SelectorOrValue<T>
where
T: Selector,
{
type Request = T::Request;
type Response = T::Response;
type EventResponse = T::EventResponse;
fn on_request(&self, request: &T::Request) -> Option<Value> {
match self {
SelectorOrValue::Value(value) => Some(value.clone().into()),
SelectorOrValue::Selector(selector) => selector.on_request(request),
}
}
fn on_response(&self, response: &T::Response) -> Option<Value> {
match self {
SelectorOrValue::Value(value) => Some(value.clone().into()),
SelectorOrValue::Selector(selector) => selector.on_response(response),
}
}
fn on_response_event(&self, response: &T::EventResponse, ctx: &Context) -> Option<Value> {
match self {
SelectorOrValue::Value(value) => Some(value.clone().into()),
SelectorOrValue::Selector(selector) => selector.on_response_event(response, ctx),
}
}
fn on_error(&self, error: &BoxError, ctx: &Context) -> Option<Value> {
match self {
SelectorOrValue::Value(value) => Some(value.clone().into()),
SelectorOrValue::Selector(selector) => selector.on_error(error, ctx),
}
}
fn on_response_field(
&self,
ty: &apollo_compiler::executable::NamedType,
field: &apollo_compiler::executable::Field,
value: &serde_json_bytes::Value,
ctx: &Context,
) -> Option<Value> {
match self {
SelectorOrValue::Value(value) => Some(value.clone().into()),
SelectorOrValue::Selector(selector) => {
selector.on_response_field(ty, field, value, ctx)
}
}
}
fn on_drop(&self) -> Option<Value> {
match self {
SelectorOrValue::Value(value) => Some(value.clone().into()),
SelectorOrValue::Selector(selector) => selector.on_drop(),
}
}
fn is_active(&self, stage: super::Stage) -> bool {
match self {
SelectorOrValue::Value(_) => true,
SelectorOrValue::Selector(selector) => selector.is_active(stage),
}
}
}
#[cfg(test)]
mod test {
use TestSelector::Req;
use TestSelector::Resp;
use TestSelector::Static;
use opentelemetry::Value;
use serde_json_bytes::json;
use tower::BoxError;
use crate::Context;
use crate::plugins::telemetry::config_new::Selector;
use crate::plugins::telemetry::config_new::Stage;
use crate::plugins::telemetry::config_new::conditions::Condition;
use crate::plugins::telemetry::config_new::conditions::SelectorOrValue;
use crate::plugins::telemetry::config_new::test::field;
use crate::plugins::telemetry::config_new::test::ty;
#[derive(Debug)]
enum TestSelector {
Req,
Resp,
Static(i64),
}
impl Selector for TestSelector {
type Request = Option<i64>;
type Response = Option<i64>;
type EventResponse = Option<i64>;
fn on_request(&self, request: &Self::Request) -> Option<Value> {
match self {
Req => request.map(Value::I64),
Resp => None,
Static(v) => Some((*v).into()),
}
}
fn on_response_event(
&self,
response: &Self::EventResponse,
_ctx: &crate::Context,
) -> Option<opentelemetry::Value> {
match self {
Req => None,
Resp => response.map(Value::I64),
Static(v) => Some((*v).into()),
}
}
fn on_response(&self, response: &Self::Response) -> Option<Value> {
match self {
Req => None,
Resp => response.map(Value::I64),
Static(v) => Some((*v).into()),
}
}
fn on_error(
&self,
error: &tower::BoxError,
_ctx: &Context,
) -> Option<opentelemetry::Value> {
if error.to_string() != "<empty>" {
Some("error".into())
} else {
None
}
}
fn on_response_field(
&self,
_ty: &apollo_compiler::executable::NamedType,
_field: &apollo_compiler::executable::Field,
value: &serde_json_bytes::Value,
_ctx: &Context,
) -> Option<Value> {
if let serde_json_bytes::Value::Number(val) = value {
Some(Value::I64(val.as_i64().expect("mut be i64")))
} else {
None
}
}
fn on_drop(&self) -> Option<Value> {
match self {
Static(v) => Some((*v).into()),
_ => None,
}
}
fn is_active(&self, stage: crate::plugins::telemetry::config_new::Stage) -> bool {
match self {
Req => matches!(stage, Stage::Request),
Resp => matches!(
stage,
Stage::Response | Stage::ResponseEvent | Stage::ResponseField
),
Static(_) => true,
}
}
}
#[test]
fn test_condition_exist() {
assert_eq!(exists(Req).req(None), Some(false));
assert_eq!(exists(Req).req(Some(1i64)), Some(true));
assert!(!exists(Resp).resp(None));
assert!(exists(Resp).resp(Some(1i64)));
assert!(!exists(Resp).resp_event(None));
assert!(exists(Resp).resp_event(Some(1i64)));
assert!(!exists(Resp).error(None));
assert!(exists(Resp).error(Some("error")));
assert!(!exists(Resp).field(None));
assert!(exists(Resp).field(Some(1i64)));
}
#[test]
fn test_condition_eq() {
assert_eq!(eq(1, 2).req(None), Some(false));
assert_eq!(eq(1, 1).req(None), Some(true));
assert!(!eq(1, 2).resp(None));
assert!(eq(1, 1).resp(None));
assert!(!eq(1, 2).resp_event(None));
assert!(eq(1, 1).resp_event(None));
assert!(!eq(1, 2).error(None));
assert!(eq(1, 1).error(None));
assert!(!eq(1, 2).field(None));
assert!(eq(1, 1).field(None));
}
#[test]
fn test_condition_gt() {
test_gt("2", "1", "1");
test_gt(2, 1, 1);
test_gt(2.0, 1.0, 1.0);
test_gt("b", "a", "a");
assert_eq!(gt(true, false).req(None), Some(true));
assert_eq!(gt(false, true).req(None), Some(false));
assert_eq!(gt(true, true).req(None), Some(false));
assert_eq!(gt(false, false).req(None), Some(false));
}
fn test_gt<T>(l1: T, l2: T, r: T)
where
T: Into<SelectorOrValue<TestSelector>> + Clone,
{
assert_eq!(gt(l1.clone(), r.clone()).req(None), Some(true));
assert_eq!(gt(l2.clone(), r.clone()).req(None), Some(false));
assert!(gt(l1.clone(), r.clone()).resp(None));
assert!(!gt(l2.clone(), r.clone()).resp(None));
assert!(gt(l1.clone(), r.clone()).resp_event(None));
assert!(!gt(l2.clone(), r.clone()).resp_event(None));
assert!(gt(l1.clone(), r.clone()).error(None));
assert!(!gt(l2.clone(), r.clone()).error(None));
assert!(gt(l1.clone(), r.clone()).field(None));
assert!(!gt(l2.clone(), r.clone()).field(None));
}
#[test]
fn test_condition_lt() {
test_lt("1", "2", "2");
test_lt(1, 2, 2);
test_lt(1.0, 2.0, 2.0);
test_lt("1.0", "2.0", "2.0");
test_lt("a", "b", "b");
assert_eq!(lt(true, false).req(None), Some(false));
assert_eq!(lt(false, true).req(None), Some(true));
assert_eq!(lt(true, true).req(None), Some(false));
assert_eq!(lt(false, false).req(None), Some(false));
}
fn test_lt<T>(l1: T, l2: T, r: T)
where
T: Into<SelectorOrValue<TestSelector>> + Clone,
{
assert_eq!(lt(l1.clone(), r.clone()).req(None), Some(true));
assert_eq!(lt(l2.clone(), r.clone()).req(None), Some(false));
assert!(lt(l1.clone(), r.clone()).resp(None));
assert!(!lt(l2.clone(), r.clone()).resp(None));
assert!(lt(l1.clone(), r.clone()).resp_event(None));
assert!(!lt(l2.clone(), r.clone()).resp_event(None));
assert!(lt(l1.clone(), r.clone()).error(None));
assert!(!lt(l2.clone(), r.clone()).error(None));
assert!(lt(l1.clone(), r.clone()).field(None));
assert!(!lt(l2.clone(), r.clone()).field(None));
}
#[test]
fn test_condition_not() {
assert_eq!(not(eq(1, 2)).req(None), Some(true));
assert_eq!(not(eq(1, 1)).req(None), Some(false));
assert!(not(eq(1, 2)).resp(None));
assert!(!not(eq(1, 1)).resp(None));
assert!(not(eq(1, 2)).resp_event(None));
assert!(!not(eq(1, 1)).resp_event(None));
assert!(not(eq(1, 2)).error(None));
assert!(!not(eq(1, 1)).error(None));
assert!(not(eq(1, 2)).field(None));
assert!(!not(eq(1, 1)).field(None));
}
#[test]
fn test_condition_all() {
assert_eq!(all(eq(1, 1), eq(1, 2)).req(None), Some(false));
assert_eq!(all(eq(1, 1), eq(2, 2)).req(None), Some(true));
assert!(!all(eq(1, 1), eq(1, 2)).resp(None));
assert!(all(eq(1, 1), eq(2, 2)).resp(None));
assert!(!all(eq(1, 1), eq(1, 2)).resp_event(None));
assert!(all(eq(1, 1), eq(2, 2)).resp_event(None));
assert!(!all(eq(1, 1), eq(1, 2)).error(None));
assert!(all(eq(1, 1), eq(2, 2)).error(None));
assert!(!all(eq(1, 1), eq(1, 2)).field(None));
assert!(all(eq(1, 1), eq(2, 2)).field(None));
}
#[test]
fn test_condition_any() {
assert_eq!(any(eq(1, 1), eq(1, 2)).req(None), Some(true));
assert_eq!(any(eq(1, 1), eq(2, 2)).req(None), Some(true));
assert!(any(eq(1, 1), eq(1, 2)).resp(None));
assert!(any(eq(1, 1), eq(2, 2)).resp(None));
assert!(any(eq(1, 1), eq(1, 2)).resp_event(None));
assert!(any(eq(1, 1), eq(2, 2)).resp_event(None));
assert!(any(eq(1, 1), eq(1, 2)).error(None));
assert!(any(eq(1, 1), eq(2, 2)).error(None));
assert!(any(eq(1, 1), eq(1, 2)).field(None));
assert!(any(eq(1, 1), eq(2, 2)).field(None));
}
#[test]
fn test_rewrite() {
let mut condition = all(eq(1, Req), eq(3, Resp));
assert!(!condition.resp(Some(3i64)));
assert!(condition.req(Some(1i64)).is_none());
assert!(condition.resp(Some(3i64)));
let mut condition = all(gt(2, Req), eq(3, Resp));
assert!(!condition.resp(Some(3i64)));
assert!(condition.req(Some(1i64)).is_none());
assert!(condition.resp(Some(3i64)));
let mut condition = all(lt(1, Req), eq(3, Resp));
assert!(!condition.resp(Some(3i64)));
assert!(condition.req(Some(2i64)).is_none());
assert!(condition.resp(Some(3i64)));
let mut condition = all(exists(Req), eq(3, Resp));
assert!(!condition.resp(Some(3i64)));
assert!(condition.req(Some(1i64)).is_none());
assert!(condition.resp(Some(3i64)));
}
#[test]
fn test_condition_selector() {
assert_eq!(eq(Req, 1).req(Some(1i64)), Some(true));
assert_eq!(eq(Req, 1).req(None), None);
assert_eq!(eq(1, Req).req(Some(1i64)), Some(true));
assert_eq!(eq(1, Req).req(None), None);
assert_eq!(eq(Req, Req).req(Some(1i64)), Some(true));
assert_eq!(eq(Req, Req).req(None), None);
assert_eq!(gt(Req, 1).req(Some(2i64)), Some(true));
assert_eq!(gt(Req, 1).req(None), None);
assert_eq!(gt("2", Req).req(Some(1i64)), Some(true));
assert_eq!(gt("2.1", Req).req(Some(1i64)), Some(true));
assert_eq!(gt(2, Req).req(Some(1i64)), Some(true));
assert_eq!(gt(2, Req).req(None), None);
assert_eq!(gt(Req, Req).req(Some(1i64)), Some(false));
assert_eq!(gt(Req, 1).req(None), None);
assert_eq!(lt(Req, 2).req(Some(1i64)), Some(true));
assert_eq!(lt(Req, 2).req(None), None);
assert_eq!(lt(1, Req).req(Some(2i64)), Some(true));
assert_eq!(lt(1, Req).req(None), None);
assert_eq!(lt(Req, Req).req(Some(1i64)), Some(false));
assert_eq!(lt(Req, Req).req(None), None);
assert_eq!(exists(Req).req(Some(1i64)), Some(true));
assert_eq!(exists(Req).req(None), Some(false));
assert!(!exists(Resp).resp(None));
assert_eq!(all(eq(1, 1), eq(1, Req)).req(Some(1i64)), Some(true));
assert_eq!(all(eq(1, 1), eq(1, Req)).req(None), None);
assert_eq!(any(eq(1, 2), eq(1, Req)).req(Some(1i64)), Some(true));
assert_eq!(any(eq(1, 2), eq(1, Req)).req(None), None);
assert!(eq(Resp, 1).resp(Some(1i64)));
assert!(eq(Resp, 1).resp_event(Some(1i64)));
assert!(eq(Resp, 1).field(Some(1i64)));
assert!(eq(Resp, "error").error(Some("error")));
}
#[test]
fn test_condition_validate() {
assert!(eq(Req, 1).validate(Some(Stage::Request)).is_ok());
assert!(eq(Req, 1).validate(Some(Stage::Response)).is_ok());
assert!(eq(1, Req).validate(Some(Stage::Request)).is_ok());
assert!(eq(1, Req).validate(Some(Stage::Response)).is_ok());
assert!(eq(Resp, 1).validate(Some(Stage::Request)).is_err());
assert!(eq(Resp, 1).validate(None).is_ok());
assert!(eq(1, Resp).validate(None).is_ok());
assert!(eq(1, Resp).validate(Some(Stage::Request)).is_err());
assert!(exists(Resp).validate(Some(Stage::Request)).is_err());
assert!(exists(Req).validate(None).is_ok());
assert!(exists(Req).validate(Some(Stage::Request)).is_ok());
assert!(exists(Resp).validate(None).is_ok());
}
#[test]
fn test_evaluate_drop() {
assert!(eq(Req, 1).evaluate_drop().is_none());
assert!(eq(1, Req).evaluate_drop().is_none());
assert_eq!(eq(1, 1).evaluate_drop(), Some(true));
assert_eq!(eq(1, 2).evaluate_drop(), Some(false));
assert_eq!(eq(Static(1), 1).evaluate_drop(), Some(true));
assert_eq!(eq(1, Static(2)).evaluate_drop(), Some(false));
assert_eq!(lt(1, 2).evaluate_drop(), Some(true));
assert_eq!(lt(2, 1).evaluate_drop(), Some(false));
assert_eq!(lt(Static(1), 2).evaluate_drop(), Some(true));
assert_eq!(lt(2, Static(1)).evaluate_drop(), Some(false));
assert_eq!(gt("2", "1").evaluate_drop(), Some(true));
assert_eq!(gt(2, 1).evaluate_drop(), Some(true));
assert_eq!(gt(1, 2).evaluate_drop(), Some(false));
assert_eq!(gt(Static(2), 1).evaluate_drop(), Some(true));
assert_eq!(gt(1, Static(2)).evaluate_drop(), Some(false));
assert_eq!(not(eq(1, 2)).evaluate_drop(), Some(true));
assert_eq!(not(eq(1, 1)).evaluate_drop(), Some(false));
assert_eq!(all(eq(1, 1), eq(2, 2)).evaluate_drop(), Some(true));
assert_eq!(all(eq(1, 1), eq(2, 1)).evaluate_drop(), Some(false));
assert_eq!(any(eq(1, 1), eq(1, 2)).evaluate_drop(), Some(true));
assert_eq!(any(eq(1, 2), eq(1, 2)).evaluate_drop(), Some(false));
}
fn exists(selector: TestSelector) -> Condition<TestSelector> {
Condition::<TestSelector>::Exists(selector)
}
fn not(selector: Condition<TestSelector>) -> Condition<TestSelector> {
Condition::<TestSelector>::Not(Box::new(selector))
}
fn eq<L, R>(left: L, right: R) -> Condition<TestSelector>
where
L: Into<SelectorOrValue<TestSelector>>,
R: Into<SelectorOrValue<TestSelector>>,
{
Condition::<TestSelector>::Eq([left.into(), right.into()])
}
fn gt<L, R>(left: L, right: R) -> Condition<TestSelector>
where
L: Into<SelectorOrValue<TestSelector>>,
R: Into<SelectorOrValue<TestSelector>>,
{
Condition::<TestSelector>::Gt([left.into(), right.into()])
}
fn lt<L, R>(left: L, right: R) -> Condition<TestSelector>
where
L: Into<SelectorOrValue<TestSelector>>,
R: Into<SelectorOrValue<TestSelector>>,
{
Condition::<TestSelector>::Lt([left.into(), right.into()])
}
fn all(l: Condition<TestSelector>, r: Condition<TestSelector>) -> Condition<TestSelector>
where {
Condition::<TestSelector>::All(vec![l, r])
}
fn any(l: Condition<TestSelector>, r: Condition<TestSelector>) -> Condition<TestSelector>
where {
Condition::<TestSelector>::Any(vec![l, r])
}
impl From<bool> for SelectorOrValue<TestSelector> {
fn from(value: bool) -> Self {
SelectorOrValue::Value(value.into())
}
}
impl From<i64> for SelectorOrValue<TestSelector> {
fn from(value: i64) -> Self {
SelectorOrValue::Value(value.into())
}
}
impl From<f64> for SelectorOrValue<TestSelector> {
fn from(value: f64) -> Self {
SelectorOrValue::Value(value.into())
}
}
impl From<&str> for SelectorOrValue<TestSelector> {
fn from(value: &str) -> Self {
SelectorOrValue::Value(value.to_string().into())
}
}
impl From<TestSelector> for SelectorOrValue<TestSelector> {
fn from(value: TestSelector) -> Self {
SelectorOrValue::Selector(value)
}
}
impl Condition<TestSelector> {
fn req(&mut self, value: Option<i64>) -> Option<bool> {
self.evaluate_request(&value)
}
fn resp(&mut self, value: Option<i64>) -> bool {
self.evaluate_response(&value)
}
fn resp_event(&mut self, value: Option<i64>) -> bool {
self.evaluate_event_response(&value, &Context::new())
}
fn error(&mut self, value: Option<&str>) -> bool {
self.evaluate_error(
&BoxError::from(value.unwrap_or("<empty>")),
&Default::default(),
)
}
fn field(&mut self, value: Option<i64>) -> bool {
match value {
None => {
self.evaluate_response_field(&ty(), field(), &json!(false), &Context::new())
}
Some(value) => {
self.evaluate_response_field(&ty(), field(), &json!(value), &Context::new())
}
}
}
}
}