1 | use crate::parser::model::{FnArg, TestFunction};
|
2 | use crate::query::queryable::Queryable;
|
3 | use crate::query::state::{Data, Pointer, State};
|
4 | use crate::query::Query;
|
5 | use regex::Regex;
|
6 | use std::borrow::Cow;
|
7 |
|
8 | impl TestFunction {
|
9 | pub fn apply<'a, T: Queryable>(&self, state: State<'a, T>) -> State<'a, T> {
|
10 | match self {
|
11 | TestFunction::Length(arg: &Box) => length(state:arg.process(state)),
|
12 | TestFunction::Count(arg: &FnArg) => count(state:arg.process(state)),
|
13 | TestFunction::Match(lhs: &FnArg, rhs: &FnArg) => {
|
14 | regex(lhs.process(state.clone()), rhs.process(state), substr:false)
|
15 | }
|
16 | TestFunction::Search(lhs: &FnArg, rhs: &FnArg) => {
|
17 | regex(lhs.process(state.clone()), rhs.process(state), substr:true)
|
18 | }
|
19 | TestFunction::Custom(name: &String, args: &Vec) => custom(name, args, state),
|
20 | TestFunction::Value(arg: &FnArg) => value(state:arg.process(state)),
|
21 | _ => State::nothing(state.root),
|
22 | }
|
23 | }
|
24 | }
|
25 |
|
26 | impl Query for FnArg {
|
27 | fn process<'a, T: Queryable>(&self, step: State<'a, T>) -> State<'a, T> {
|
28 | match self {
|
29 | FnArg::Literal(lit: &Literal) => lit.process(state:step),
|
30 | FnArg::Test(test: &Box) => test.process(state:step),
|
31 | FnArg::Filter(filter: &Filter) => filter.process(state:step),
|
32 | }
|
33 | }
|
34 | }
|
35 |
|
36 | impl Query for TestFunction {
|
37 | fn process<'a, T: Queryable>(&self, step: State<'a, T>) -> State<'a, T> {
|
38 | self.apply(state:step)
|
39 | }
|
40 | }
|
41 |
|
42 | fn custom<'a, T: Queryable>(name: &str, args: &Vec<FnArg>, state: State<'a, T>) -> State<'a, T> {
|
43 | let args: Vec> = argsimpl Iterator- >
|
44 | .into_iter()
|
45 | .map(|v: &FnArg| v.process(state.clone()))
|
46 | .flat_map(|v: State<'_, T>| match v.data {
|
47 | Data::Value(v: T) => vec![Cow::Owned(v)],
|
48 | Data::Ref(Pointer { inner: &T, .. }) => vec![Cow::Borrowed(inner)],
|
49 | Data::Refs(v: Vec>) => v.into_iter().map(|v: Pointer<'_, T>| Cow::Borrowed(v.inner)).collect(),
|
50 | _ => vec![],
|
51 | })
|
52 | .collect::<Vec<_>>();
|
53 |
|
54 | State::data(
|
55 | state.root,
|
56 | Data::Value(Queryable::extension_custom(name, args)),
|
57 | )
|
58 | }
|
59 |
|
60 | /// Returns the length/size of the object.
|
61 | ///
|
62 | /// # Returns
|
63 | ///
|
64 | /// Returns a `Progress` enum containing either:
|
65 | /// - `Progress::Data` with a vector of references to self and the query path for strings/arrays/objects
|
66 | /// - `Progress::Nothing` for other types
|
67 | ///
|
68 | /// The returned length follows JSON path length() function semantics based on the type:
|
69 | /// - String type: Number of Unicode scalar values
|
70 | /// - Array type: Number of elements
|
71 | /// - Object type: Number of members
|
72 | /// - Other types: Nothing
|
73 | fn length<T: Queryable>(state: State<T>) -> State<T> {
|
74 | let from_item: impl Fn(&T) -> State<'_, T> = |item: &T| {
|
75 | if let Some(v: &str) = item.as_str() {
|
76 | State::i64(i:v.chars().count() as i64, state.root)
|
77 | } else if let Some(items: &Vec) = item.as_array() {
|
78 | State::i64(i:items.len() as i64, state.root)
|
79 | } else if let Some(items: Vec<(&String, &T)>) = item.as_object() {
|
80 | State::i64(i:items.len() as i64, state.root)
|
81 | } else {
|
82 | State::nothing(state.root)
|
83 | }
|
84 | };
|
85 |
|
86 | match state.data {
|
87 | Data::Ref(Pointer { inner: &T, .. }) => from_item(item:inner),
|
88 | Data::Refs(items: Vec>) => State::i64(i:items.len() as i64, state.root),
|
89 | Data::Value(item: T) => from_item(&item),
|
90 | Data::Nothing => State::nothing(state.root),
|
91 | }
|
92 | }
|
93 |
|
94 | /// The count() function extension provides a way
|
95 | /// to obtain the number of nodes in a nodelist
|
96 | /// and make that available for further processing in the filter expression
|
97 | fn count<T: Queryable>(state: State<T>) -> State<T> {
|
98 | let to_state: impl Fn(i64) -> State<'_, …> = |count: i64| State::i64(i:count, state.root);
|
99 |
|
100 | match state.data {
|
101 | Data::Ref(..) | Data::Value(..) => to_state(count:1),
|
102 | Data::Refs(items: Vec>) => to_state(count:items.len() as i64),
|
103 | Data::Nothing => State::nothing(state.root),
|
104 | }
|
105 | }
|
106 | /// The match() function extension provides
|
107 | /// a way to check whether (the entirety of; see Section 2.4.7)
|
108 | /// a given string matches a given regular expression,
|
109 | /// which is in the form described in [RFC9485].
|
110 | ///
|
111 | /// Its arguments are instances of ValueType
|
112 | /// (possibly taken from a singular query,
|
113 | /// as for the first argument in the example above).
|
114 | /// If the first argument is not a string
|
115 | /// or the second argument is not a string conforming to [RFC9485],
|
116 | /// the result is LogicalFalse. Otherwise, the string that is the first argument is matched against
|
117 | /// the I-Regexp contained in the string that is the second argument; the result is LogicalTrue
|
118 | /// if the string matches the I-Regexp and is LogicalFalse otherwise.
|
119 | fn regex<'a, T: Queryable>(lhs: State<'a, T>, rhs: State<'a, T>, substr: bool) -> State<'a, T> {
|
120 | let to_state: impl Fn(bool) -> State<'_, …> = |b: bool| State::bool(b, lhs.root);
|
121 | let regex: impl Fn(&str, Regex) -> bool = |v: &str, r: Regex| {
|
122 | if substr {
|
123 | r.find(haystack:v).is_some()
|
124 | } else {
|
125 | r.is_match(haystack:v)
|
126 | }
|
127 | };
|
128 | let to_str: impl Fn(State<'a, T>) -> … = |s: State<'a, T>| match s.data {
|
129 | Data::Value(v: T) => v.as_str().map(|s: &str| s.to_string()),
|
130 | Data::Ref(Pointer { inner: &T, .. }) => inner.as_str().map(|s: &str| s.to_string()),
|
131 | _ => None,
|
132 | };
|
133 |
|
134 | match (to_str(lhs), to_str(rhs)) {
|
135 | (Some(lhs: String), Some(rhs: String)) => Regex::new(&prepare_regex(rhs, substr))
|
136 | .map(|re| to_state(regex(&lhs, re)))
|
137 | .unwrap_or(default:to_state(false)),
|
138 | _ => to_state(false),
|
139 | }
|
140 | }
|
141 |
|
142 | fn prepare_regex(pattern: String, substring: bool) -> String {
|
143 | let pattern: String = if !substring {
|
144 | let pattern: String = if pattern.starts_with('^' ) {
|
145 | pattern
|
146 | } else {
|
147 | format!("^ {}" , pattern)
|
148 | };
|
149 | let pattern: String = if pattern.ends_with('$' ) {
|
150 | pattern
|
151 | } else {
|
152 | format!(" {}$" , pattern)
|
153 | };
|
154 | pattern
|
155 | } else {
|
156 | pattern.to_string()
|
157 | };
|
158 | let pattern: String = if pattern.contains(" \\\\" ) {
|
159 | pattern.replace(from:" \\\\" , to:" \\" )
|
160 | } else {
|
161 | pattern.to_string()
|
162 | };
|
163 |
|
164 | pattern.trim_matches(|c: char| c == ' \'' || c == '"' ).to_string()
|
165 | }
|
166 |
|
167 | fn value<T: Queryable>(state: State<T>) -> State<T> {
|
168 | match state.data {
|
169 | Data::Ref(..) | Data::Value(..) => state,
|
170 | Data::Refs(items: Vec>) if items.len() == 1 => {
|
171 | State::data(state.root, Data::Ref(items[0].clone()))
|
172 | }
|
173 | _ => State::nothing(state.root),
|
174 | }
|
175 | }
|
176 |
|
177 | #[cfg (test)]
|
178 | mod tests {
|
179 | use crate::parser::model::Segment;
|
180 | use crate::parser::model::Selector;
|
181 | use crate::parser::model::Test;
|
182 | use crate::parser::model::TestFunction;
|
183 | use crate::query::state::{Data, Pointer, State};
|
184 | use crate::query::test_function::{regex, FnArg};
|
185 | use crate::query::Query;
|
186 | use crate::{arg, q_segment, segment, selector, test, test_fn};
|
187 | use serde_json::json;
|
188 |
|
189 | #[test ]
|
190 | fn test_len() {
|
191 | let json = json!({"array" : [1,2,3]});
|
192 | let state = State::root(&json);
|
193 |
|
194 | let query = test_fn!(length arg!(t test!(@ segment!(selector!(array)))));
|
195 | let res = query.process(state);
|
196 |
|
197 | assert_eq!(res.ok_val(), Some(json!(3)));
|
198 | }
|
199 |
|
200 | #[test ]
|
201 | fn test_match_1() {
|
202 | let json = json!({"a" : "abc sdgfudsf" ,"b" : "abc.*" });
|
203 | let state = State::root(&json);
|
204 |
|
205 | let query = test_fn!(match
|
206 | arg!(t test!(@ segment!(selector!(a)))),
|
207 | arg!(t test!(@ segment!(selector!(b))))
|
208 | );
|
209 | let res = query.process(state);
|
210 |
|
211 | assert_eq!(res.ok_val(), Some(json!(true)));
|
212 | }
|
213 |
|
214 | #[test ]
|
215 | fn test_count_1() {
|
216 | let json = json!({"array" : [1,2,3]});
|
217 | let state = State::root(&json);
|
218 |
|
219 | let query = test_fn!(count arg!(t test!(@ segment!(selector!(array)))));
|
220 | let res = query.process(state);
|
221 |
|
222 | assert_eq!(res.ok_val(), Some(json!(1)));
|
223 | }
|
224 |
|
225 | #[test ]
|
226 | fn test_search() {
|
227 | let json = json!("123" );
|
228 | let state = State::root(&json);
|
229 | let reg = State::str("[a-z]+" , &json);
|
230 |
|
231 | let res = regex(state, reg, true);
|
232 |
|
233 | assert_eq!(res.ok_val(), Some(json!(false)));
|
234 | }
|
235 |
|
236 | #[test ]
|
237 | fn test_match() {
|
238 | let json = json!("bbab" );
|
239 | let state = State::root(&json);
|
240 | let reg = State::str("^b.?b$" , &json);
|
241 |
|
242 | let res = regex(state, reg, false);
|
243 |
|
244 | assert_eq!(res.ok_val(), Some(json!(false)));
|
245 | }
|
246 | }
|
247 | |