use serde::{Deserialize, Serialize};
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum Operator {
And,
Or,
Not,
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Filter<T> {
Operator(FilterOperator<T>),
Condition(T),
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FilterOperator<T> {
pub operator: Operator,
pub conditions: Vec<Filter<T>>,
}
impl<T> FilterOperator<T> {
pub fn new(operator: Operator, conditions: Vec<Filter<T>>) -> Self {
Self {
operator,
conditions,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
struct Cond {
#[serde(skip_serializing_if = "Option::is_none")]
pub has_keyword: Option<String>,
}
#[test]
fn filter_operator_or_roundtrip() {
let json =
r#"{"operator":"OR","conditions":[{"has_keyword":"music"},{"has_keyword":"video"}]}"#;
let f: Filter<Cond> = serde_json::from_str(json).expect("must parse");
match &f {
Filter::Operator(op) => {
assert_eq!(op.operator, Operator::Or);
assert_eq!(op.conditions.len(), 2);
}
other => panic!("expected Operator, got {other:?}"),
}
let back = serde_json::to_string(&f).expect("must serialize");
let f2: Filter<Cond> = serde_json::from_str(&back).expect("roundtrip");
assert_eq!(f, f2);
}
#[test]
fn filter_condition_deserialization() {
let json = r#"{"has_keyword":"$seen"}"#;
let f: Filter<Cond> = serde_json::from_str(json).expect("must parse");
match &f {
Filter::Condition(c) => assert_eq!(c.has_keyword.as_deref(), Some("$seen")),
other => panic!("expected Condition, got {other:?}"),
}
}
#[test]
fn operator_serialization() {
assert_eq!(serde_json::to_string(&Operator::And).unwrap(), r#""AND""#);
assert_eq!(serde_json::to_string(&Operator::Or).unwrap(), r#""OR""#);
assert_eq!(serde_json::to_string(&Operator::Not).unwrap(), r#""NOT""#);
}
#[test]
fn nested_filter_roundtrip() {
let filter = Filter::Operator(FilterOperator {
operator: Operator::And,
conditions: vec![
Filter::Operator(FilterOperator {
operator: Operator::Or,
conditions: vec![
Filter::Condition(Cond {
has_keyword: Some("a".to_owned()),
}),
Filter::Condition(Cond {
has_keyword: Some("b".to_owned()),
}),
],
}),
Filter::Condition(Cond {
has_keyword: Some("c".to_owned()),
}),
],
});
let json = serde_json::to_string(&filter).expect("serialize");
let back: Filter<Cond> = serde_json::from_str(&json).expect("deserialize");
assert_eq!(filter, back);
}
}