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