1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
use serde::{Deserialize, Serialize};

pub mod first_order;
use first_order::FirstOrder;
use serde_json::Value;

use self::{context::PredicateContext, error::PredicateError, second_order::SecondOrder};

pub mod context;
pub mod second_order;

mod error;

/// A Predicate is an Object whose members describe a testable
/// condition that evaluates as either true or false.
///
/// The essential components of a Predicate include:
///
/// - A label identifying the predicate operation,
/// - A reference to the value being tested, and
/// - The condition against which the referenced value is to be evaluated.
///
/// Predicate objects MUST have exactly one "op" member whose value
/// indicates the type of predicate operation to perform.  It's value
/// MUST be one of: "and", "contains", "contains-", "defined", "ends",
/// "ends-", "in", "in-", "less", "matches", "matches-", "more", "not",
/// "or", "starts", "starts-", "test", "test-", "type", or "undefined".
///
/// The semantics for each are defined in the sections that follow.
///
/// Note that the value of the "op" member is case-sensitive and that
/// each of the operations listed are in lower-case.  The value "Starts",
/// for example, is not equivalent to "starts".
///
/// If the "op" member specifies any value other than one of those listed
/// above, the evaluation of the predicate operation MUST cease and be
/// handled as if a boolean value of "false" was returned.  The
/// application processing the predicate operation MAY signal that an
/// error condition has occurred depending on the specific requirements
/// of the application within which JSON Predicates are being used.
///
/// The remaining structure of each predicate operation depends on the
/// specific type.  There are two basic types of predicates.
///
/// -  First Order Predicates that are used to test one name value pair
///    against a single condition, and
///
/// -  Second Order Predicates that aggregate one or more subordinate
///    First or Second Order Predicates.
///
/// In addition to the required "op" member, First Order Predicates have
/// exactly one "path" member whose value MUST be a string containing a
/// JSON-Pointer [RFC6901](https://datatracker.ietf.org/doc/html/rfc6901)
/// value referencing the name value pair that is to be tested.  If the
/// "path" member is not specified within the
/// predicate object, it's value is assumed to be an empty string.
///
/// Second Order Predicates MUST have exactly one "apply" member whose
/// value is a JSON Array containing one or more First or Second Order
/// Predicate Objects.
///
///
/// Additional members can be required depending on the specific
/// predicate operation.  All other members not explicitly defined by
/// this specification MUST be ignored.
///
/// Note that the ordering of members in JSON objects is not significant;
/// therefore the following operations are equivalent:
///
/// ```json
/// {"op": "contains", "path": "/a/b/c", "value": "ABC"}
/// {"path": "/a/b/c", "op": "contains", "value": "ABC"}
/// {"value": "ABC", "path": "/a/b/c", "op": "contains"}
/// ```
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
#[serde(untagged)]
pub enum Predicate {
    FirstOrder(FirstOrder),
    SecondOrder(SecondOrder),
}

impl PredicateImpl for Predicate {
    fn evaluate(&self, data: &Value, ctx: PredicateContext) -> Result<bool, PredicateError> {
        match self {
            Predicate::FirstOrder(fo) => fo.evaluate(data, ctx),
            Predicate::SecondOrder(fo) => fo.evaluate(data, ctx),
        }
    }
}

pub trait PredicateImpl {
    /// Evaluate the predicate against the provided JSON
    ///
    /// The error result means the predicate couldn't be evaluated properly so
    /// you have to assume it's a false result and you'll also have more information about why it
    /// didn't validate.
    fn evaluate(&self, data: &Value, ctx: PredicateContext) -> Result<bool, PredicateError>;

    /// Evaluate the predicate against the provided JSON
    fn test(&self, data: &Value, ctx: PredicateContext) -> bool {
        self.evaluate(data, ctx).unwrap_or(false)
    }
}