json_predicate/predicate/first_order/
end.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 "ends" predicate evaluates as true if the referenced element is defined
16/// and has a value whose string representation ends with the exact sequence of
17/// characters given by the predicate object's "value" member.
18#[derive(Debug, Clone, PartialEq, Eq, Builder)]
19#[builder(pattern = "owned", setter(into, strip_option))]
20pub struct End {
21    /// Must be a [JSON Pointer](https://tools.ietf.org/html/rfc6901)
22    /// If the "path" member is not specified within the predicate object, it's value is assumed to be an empty string.
23    pub path: Option<JSONPath>,
24    #[builder(default)]
25    pub ignore_case: bool,
26    pub value: serde_json::Value,
27}
28
29impl Serialize for End {
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("End", 3)?;
35        if self.ignore_case {
36            state.serialize_field("op", "end-")?;
37        } else {
38            state.serialize_field("op", "end")?;
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 End {
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 VisitorEnd<'de> {
90            marker: PhantomData<End>,
91            lifetime: PhantomData<&'de ()>,
92        }
93
94        impl<'de> Visitor<'de> for VisitorEnd<'de> {
95            type Value = End;
96            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
97                formatter.write_str("End")
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                    "end" => false,
139                    "end-" => true,
140                    _ => {
141                        return Err(serde::de::Error::custom(
142                            "`op` should be either `end` or `end-`",
143                        ));
144                    }
145                };
146
147                Ok(End {
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            "End",
159            FIELDS,
160            VisitorEnd {
161                marker: PhantomData::<End>,
162                lifetime: PhantomData,
163            },
164        )
165    }
166}
167
168impl From<End> for FirstOrder {
169    fn from(value: End) -> Self {
170        FirstOrder::End(value)
171    }
172}
173
174impl PredicateImpl for End {
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) {
183            (Value::String(context), Value::String(value)) => {
184                if self.ignore_case {
185                    Ok(context.to_lowercase().ends_with(&value.to_lowercase()))
186                } else {
187                    Ok(context.ends_with(value))
188                }
189            }
190            _ => Ok(false),
191        }
192    }
193}
194
195#[cfg(test)]
196mod tests {
197    use serde::Deserialize;
198
199    use crate::{json_path::JSONPath, predicate::first_order::end::End};
200
201    #[test]
202    fn snapshot_test() {
203        let end_expect = serde_json::json!({
204             "op": "end",
205             "path": "/a/b",
206             "value": " is a "
207        });
208
209        let end = End {
210            path: Some(JSONPath::new("/a/b").unwrap()),
211            ignore_case: false,
212            value: serde_json::Value::String(" is a ".to_string()),
213        };
214
215        assert_eq!(serde_json::to_value(end).unwrap(), end_expect);
216    }
217
218    #[test]
219    fn deser_test() {
220        let end_expect = serde_json::json!({
221             "op": "end",
222             "path": "/a/b",
223             "value": " is a "
224        });
225
226        let end = End {
227            path: Some(JSONPath::new("/a/b").unwrap()),
228            ignore_case: false,
229            value: serde_json::Value::String(" is a ".to_string()),
230        };
231
232        let deser = End::deserialize(end_expect).unwrap();
233
234        assert_eq!(end, deser);
235    }
236}