json_predicate/predicate/first_order/
test.rs

1use derive_builder::Builder;
2use serde_json::Value;
3use std::marker::PhantomData;
4
5use serde::de::{self, Deserialize, Deserializer, MapAccess, Visitor};
6use serde::{ser::SerializeStruct, Serialize};
7
8use crate::json_path::JSONPath;
9use crate::predicate::context::PredicateContext;
10use crate::predicate::error::PredicateError;
11use crate::predicate::PredicateImpl;
12
13use super::FirstOrder;
14
15/// The JSON Patch "test" operation, as defined by [RFC6902, Section 4.6](https://datatracker.ietf.org/doc/html/rfc6902#section-4.6),
16/// can be used as a First Order Predicate operation.
17/// It evaluates as true if the referenced element exists and specifies a value
18/// that is exactly equal to that provided by the predicate's "value" member.
19/// The rules for evaluating equality are identical to those defined within [RFC6902, Section 4.6](https://datatracker.ietf.org/doc/html/rfc6902#section-4.6),
20/// with one exception given for optional case-insensitive comparisons.
21#[derive(Debug, Clone, PartialEq, Eq, Builder)]
22#[builder(pattern = "owned", setter(into, strip_option))]
23pub struct Test {
24    /// Must be a [JSON Pointer](https://tools.ietf.org/html/rfc6901)
25    /// If the "path" member is not specified within the predicate object, it's value is assumed to be an empty string.
26    pub path: Option<JSONPath>,
27    #[builder(default)]
28    pub ignore_case: bool,
29    pub value: serde_json::Value,
30}
31
32impl Serialize for Test {
33    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
34    where
35        S: serde::Serializer,
36    {
37        let mut state = serializer.serialize_struct("Test", 3)?;
38        if self.ignore_case {
39            state.serialize_field("op", "test-")?;
40        } else {
41            state.serialize_field("op", "test")?;
42        }
43        state.serialize_field("path", &self.path)?;
44        state.serialize_field("value", &self.value)?;
45        state.end()
46    }
47}
48
49impl<'de> Deserialize<'de> for Test {
50    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
51    where
52        D: Deserializer<'de>,
53    {
54        #[allow(non_camel_case_types)]
55        enum Field {
56            op,
57            path,
58            value,
59            __ignore,
60        }
61        struct FieldVisitor;
62
63        impl<'de> Visitor<'de> for FieldVisitor {
64            type Value = Field;
65            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
66                formatter.write_str("field identifier")
67            }
68
69            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
70            where
71                E: de::Error,
72            {
73                match value {
74                    "op" => Ok(Field::op),
75                    "path" => Ok(Field::path),
76                    "value" => Ok(Field::value),
77                    _ => Ok(Field::__ignore),
78                }
79            }
80        }
81
82        impl<'de> Deserialize<'de> for Field {
83            #[inline]
84            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
85            where
86                D: Deserializer<'de>,
87            {
88                Deserializer::deserialize_identifier(deserializer, FieldVisitor)
89            }
90        }
91
92        struct VisitorTest<'de> {
93            marker: PhantomData<Test>,
94            lifetime: PhantomData<&'de ()>,
95        }
96
97        impl<'de> Visitor<'de> for VisitorTest<'de> {
98            type Value = Test;
99            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
100                formatter.write_str("Test")
101            }
102
103            #[inline]
104            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
105            where
106                A: MapAccess<'de>,
107            {
108                let mut path: Option<Option<JSONPath>> = None;
109                let mut value: Option<serde_json::Value> = None;
110                let mut op: Option<String> = None;
111
112                while let Some(key) = MapAccess::next_key::<Field>(&mut map)? {
113                    match key {
114                        Field::op => {
115                            if op.is_some() {
116                                return Err(serde::de::Error::duplicate_field("op"));
117                            }
118                            op = Some(MapAccess::next_value::<String>(&mut map)?);
119                        }
120                        Field::path => {
121                            if path.is_some() {
122                                return Err(serde::de::Error::duplicate_field("path"));
123                            }
124                            path = Some(MapAccess::next_value::<Option<JSONPath>>(&mut map)?);
125                        }
126                        Field::value => {
127                            if value.is_some() {
128                                return Err(serde::de::Error::duplicate_field("value"));
129                            }
130                            value = Some(MapAccess::next_value::<serde_json::Value>(&mut map)?);
131                        }
132                        Field::__ignore => {}
133                    }
134                }
135
136                let path = path.ok_or(serde::de::Error::missing_field("path"))?;
137                let value = value.ok_or(serde::de::Error::missing_field("value"))?;
138                let op = op.ok_or(serde::de::Error::missing_field("op"))?;
139
140                let ignore_case = match op.as_str() {
141                    "test" => false,
142                    "test-" => true,
143                    _ => {
144                        return Err(serde::de::Error::custom(
145                            "`op` should be either `test` or `test-`",
146                        ));
147                    }
148                };
149
150                Ok(Test {
151                    path,
152                    ignore_case,
153                    value,
154                })
155            }
156        }
157
158        const FIELDS: &[&str] = &["path", "op", "value"];
159        Deserializer::deserialize_struct(
160            deserializer,
161            "Test",
162            FIELDS,
163            VisitorTest {
164                marker: PhantomData::<Test>,
165                lifetime: PhantomData,
166            },
167        )
168    }
169}
170
171impl From<Test> for FirstOrder {
172    fn from(value: Test) -> Self {
173        FirstOrder::Test(value)
174    }
175}
176
177impl PredicateImpl for Test {
178    fn evaluate(&self, data: &Value, ctx: PredicateContext) -> Result<bool, PredicateError> {
179        let path = ctx.final_path(&self.path).unwrap_or(JSONPath::empty());
180        let ptr = path.take();
181
182        let value = &self.value;
183        let context_value = ptr.get(data)?;
184
185        if self.ignore_case {
186            Ok(context_value.to_string().to_lowercase() == value.to_string().to_lowercase())
187        } else {
188            Ok(context_value == value)
189        }
190    }
191}
192
193#[cfg(test)]
194mod tests {
195    use serde::Deserialize;
196
197    use crate::{json_path::JSONPath, predicate::first_order::test::Test};
198
199    #[test]
200    fn snapshot_test() {
201        let end_expect = serde_json::json!({
202             "op": "test",
203             "path": "/a/b",
204             "value": "val"
205        });
206
207        let end = Test {
208            path: Some(JSONPath::new("/a/b").unwrap()),
209            ignore_case: false,
210            value: serde_json::Value::String("val".to_string()),
211        };
212
213        assert_eq!(serde_json::to_value(end).unwrap(), end_expect);
214    }
215
216    #[test]
217    fn deser_test() {
218        let end_expect = serde_json::json!({
219             "op": "test",
220             "path": "/a/b",
221             "value": "val"
222        });
223
224        let end = Test {
225            path: Some(JSONPath::new("/a/b").unwrap()),
226            ignore_case: false,
227            value: serde_json::Value::String("val".to_string()),
228        };
229
230        let deser = Test::deserialize(end_expect).unwrap();
231
232        assert_eq!(end, deser);
233    }
234}