1use serde_json::Value;
2
3pub fn matches(metadata: &Value, filter: &Value) -> bool {
8 let (meta_obj, filter_obj) = match (metadata, filter) {
9 (Value::Object(m), Value::Object(f)) => (m, f),
10 _ => return false,
11 };
12 for (key, condition) in filter_obj {
13 let field = meta_obj.get(key);
14 match condition {
15 Value::Object(ops) => {
16 for (op, operand) in ops {
17 if !apply_op(op, field, operand) {
18 return false;
19 }
20 }
21 }
22 expected => {
23 if field != Some(expected) {
24 return false;
25 }
26 }
27 }
28 }
29 true
30}
31
32fn apply_op(op: &str, field: Option<&Value>, operand: &Value) -> bool {
33 match op {
34 "$eq" => field == Some(operand),
35 "$ne" => field != Some(operand),
36 "$exists" => field.is_some() == operand.as_bool().unwrap_or(true),
37 "$gt" => cmp_num(field, operand, |a, b| a > b),
38 "$gte" => cmp_num(field, operand, |a, b| a >= b),
39 "$lt" => cmp_num(field, operand, |a, b| a < b),
40 "$lte" => cmp_num(field, operand, |a, b| a <= b),
41 "$in" => match (field, operand) {
42 (Some(v), Value::Array(arr)) => arr.contains(v),
43 _ => false,
44 },
45 "$nin" => match (field, operand) {
46 (Some(v), Value::Array(arr)) => !arr.contains(v),
47 _ => true,
48 },
49 _ => false,
50 }
51}
52
53fn cmp_num<F>(field: Option<&Value>, operand: &Value, cmp: F) -> bool
54where
55 F: Fn(f64, f64) -> bool,
56{
57 match (field, operand) {
58 (Some(Value::Number(a)), Value::Number(b)) => match (a.as_f64(), b.as_f64()) {
59 (Some(av), Some(bv)) => cmp(av, bv),
60 _ => false,
61 },
62 _ => false,
63 }
64}