json_predicate/predicate/first_order/
contains.rs1use 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#[derive(Debug, Clone, PartialEq, Eq, Builder)]
20#[builder(pattern = "owned", setter(into, strip_option))]
21pub struct Contains {
22 pub path: Option<JSONPath>,
25 pub ignore_case: bool,
26 pub value: serde_json::Value,
27}
28
29impl Serialize for Contains {
30 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
31 where
32 S: serde::Serializer,
33 {
34 let mut state = serializer.serialize_struct("Contains", 3)?;
35 if self.ignore_case {
36 state.serialize_field("op", "contains-")?;
37 } else {
38 state.serialize_field("op", "contains")?;
39 }
40 state.serialize_field("path", &self.path)?;
41 state.serialize_field("value", &self.value)?;
42 state.end()
43 }
44}
45
46impl<'de> Deserialize<'de> for Contains {
47 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
48 where
49 D: Deserializer<'de>,
50 {
51 #[allow(non_camel_case_types)]
52 enum Field {
53 op,
54 path,
55 value,
56 __ignore,
57 }
58 struct FieldVisitor;
59
60 impl<'de> Visitor<'de> for FieldVisitor {
61 type Value = Field;
62 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
63 formatter.write_str("field identifier")
64 }
65
66 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
67 where
68 E: de::Error,
69 {
70 match value {
71 "op" => Ok(Field::op),
72 "path" => Ok(Field::path),
73 "value" => Ok(Field::value),
74 _ => Ok(Field::__ignore),
75 }
76 }
77 }
78
79 impl<'de> Deserialize<'de> for Field {
80 #[inline]
81 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
82 where
83 D: Deserializer<'de>,
84 {
85 Deserializer::deserialize_identifier(deserializer, FieldVisitor)
86 }
87 }
88
89 struct VisitorContains<'de> {
90 marker: PhantomData<Contains>,
91 lifetime: PhantomData<&'de ()>,
92 }
93
94 impl<'de> Visitor<'de> for VisitorContains<'de> {
95 type Value = Contains;
96 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
97 formatter.write_str("Contains")
98 }
99
100 #[inline]
101 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
102 where
103 A: MapAccess<'de>,
104 {
105 let mut path: Option<Option<JSONPath>> = None;
106 let mut value: Option<serde_json::Value> = None;
107 let mut op: Option<String> = None;
108
109 while let Some(key) = MapAccess::next_key::<Field>(&mut map)? {
110 match key {
111 Field::op => {
112 if op.is_some() {
113 return Err(serde::de::Error::duplicate_field("op"));
114 }
115 op = Some(MapAccess::next_value::<String>(&mut map)?);
116 }
117 Field::path => {
118 if path.is_some() {
119 return Err(serde::de::Error::duplicate_field("path"));
120 }
121 path = Some(MapAccess::next_value::<Option<JSONPath>>(&mut map)?);
122 }
123 Field::value => {
124 if value.is_some() {
125 return Err(serde::de::Error::duplicate_field("value"));
126 }
127 value = Some(MapAccess::next_value::<serde_json::Value>(&mut map)?);
128 }
129 Field::__ignore => {}
130 }
131 }
132
133 let path = path.ok_or(serde::de::Error::missing_field("path"))?;
134 let value = value.ok_or(serde::de::Error::missing_field("value"))?;
135 let op = op.ok_or(serde::de::Error::missing_field("op"))?;
136
137 let ignore_case = match op.as_str() {
138 "contains" => false,
139 "contains-" => true,
140 _ => {
141 return Err(serde::de::Error::custom(
142 "`op` should be either `contains` or `contains-`",
143 ));
144 }
145 };
146
147 Ok(Contains {
148 path,
149 ignore_case,
150 value,
151 })
152 }
153 }
154
155 const FIELDS: &[&str] = &["path", "op", "value"];
156 Deserializer::deserialize_struct(
157 deserializer,
158 "Contains",
159 FIELDS,
160 VisitorContains {
161 marker: PhantomData::<Contains>,
162 lifetime: PhantomData,
163 },
164 )
165 }
166}
167
168impl From<Contains> for FirstOrder {
169 fn from(value: Contains) -> Self {
170 FirstOrder::Contains(value)
171 }
172}
173
174impl PredicateImpl for Contains {
175 fn evaluate(&self, data: &Value, ctx: PredicateContext) -> Result<bool, PredicateError> {
176 let path = ctx.final_path(&self.path).unwrap_or(JSONPath::empty());
177 let ptr = path.take();
178
179 let value = &self.value;
180 let context_value = ptr.get(data)?;
181
182 match (context_value, value) {
187 (Value::String(context), Value::String(value)) => {
188 if self.ignore_case {
189 Ok(context.to_lowercase().contains(&value.to_lowercase()))
190 } else {
191 Ok(context.contains(value))
192 }
193 }
194 _ => Ok(false),
195 }
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use serde::Deserialize;
202
203 use crate::{json_path::JSONPath, predicate::first_order::contains::Contains};
204
205 #[test]
206 fn snapshot_test() {
207 let contains_expect = serde_json::json!({
208 "op": "contains",
209 "path": "/a/b",
210 "value": " is a "
211 });
212
213 let contains = Contains {
214 path: Some(JSONPath::new("/a/b").unwrap()),
215 ignore_case: false,
216 value: serde_json::Value::String(" is a ".to_string()),
217 };
218
219 assert_eq!(serde_json::to_value(contains).unwrap(), contains_expect);
220 }
221
222 #[test]
223 fn deser_test() {
224 let contains_expect = serde_json::json!({
225 "op": "contains",
226 "path": "/a/b",
227 "value": " is a "
228 });
229
230 let contains = Contains {
231 path: Some(JSONPath::new("/a/b").unwrap()),
232 ignore_case: false,
233 value: serde_json::Value::String(" is a ".to_string()),
234 };
235
236 let deser = Contains::deserialize(contains_expect).unwrap();
237
238 assert_eq!(contains, deser);
239 }
240}