1use crate::parser::model::{FnArg, TestFunction};
2use crate::query::queryable::Queryable;
3use crate::query::state::{Data, Pointer, State};
4use crate::query::Query;
5use regex::Regex;
6use std::borrow::Cow;
7
8impl TestFunction {
9 pub fn apply<'a, T: Queryable>(&self, state: State<'a, T>) -> State<'a, T> {
10 match self {
11 TestFunction::Length(arg) => length(arg.process(state)),
12 TestFunction::Count(arg) => count(arg.process(state)),
13 TestFunction::Match(lhs, rhs) => {
14 regex(lhs.process(state.clone()), rhs.process(state), false)
15 }
16 TestFunction::Search(lhs, rhs) => {
17 regex(lhs.process(state.clone()), rhs.process(state), true)
18 }
19 TestFunction::Custom(name, args) => custom(name, args, state),
20 TestFunction::Value(arg) => value(arg.process(state)),
21 _ => State::nothing(state.root),
22 }
23 }
24}
25
26impl Query for FnArg {
27 fn process<'a, T: Queryable>(&self, step: State<'a, T>) -> State<'a, T> {
28 match self {
29 FnArg::Literal(lit) => lit.process(step),
30 FnArg::Test(test) => test.process(step),
31 FnArg::Filter(filter) => filter.process(step),
32 }
33 }
34}
35
36impl Query for TestFunction {
37 fn process<'a, T: Queryable>(&self, step: State<'a, T>) -> State<'a, T> {
38 self.apply(step)
39 }
40}
41
42fn custom<'a, T: Queryable>(name: &str, args: &Vec<FnArg>, state: State<'a, T>) -> State<'a, T> {
43 let args = args
44 .into_iter()
45 .map(|v| v.process(state.clone()))
46 .flat_map(|v| match v.data {
47 Data::Value(v) => vec![Cow::Owned(v)],
48 Data::Ref(Pointer { inner, .. }) => vec![Cow::Borrowed(inner)],
49 Data::Refs(v) => v.into_iter().map(|v| 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
60fn length<T: Queryable>(state: State<T>) -> State<T> {
74 let from_item = |item: &T| {
75 if let Some(v) = item.as_str() {
76 State::i64(v.chars().count() as i64, state.root)
77 } else if let Some(items) = item.as_array() {
78 State::i64(items.len() as i64, state.root)
79 } else if let Some(items) = item.as_object() {
80 State::i64(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, .. }) => from_item(inner),
88 Data::Refs(items) => State::i64(items.len() as i64, state.root),
89 Data::Value(item) => from_item(&item),
90 Data::Nothing => State::nothing(state.root),
91 }
92}
93
94fn count<T: Queryable>(state: State<T>) -> State<T> {
98 let to_state = |count: i64| State::i64(count, state.root);
99
100 match state.data {
101 Data::Ref(..) | Data::Value(..) => to_state(1),
102 Data::Refs(items) => to_state(items.len() as i64),
103 Data::Nothing => State::nothing(state.root),
104 }
105}
106fn regex<'a, T: Queryable>(lhs: State<'a, T>, rhs: State<'a, T>, substr: bool) -> State<'a, T> {
120 let to_state = |b| State::bool(b, lhs.root);
121 let regex = |v: &str, r: Regex| {
122 if substr {
123 r.find(v).is_some()
124 } else {
125 r.is_match(v)
126 }
127 };
128 let to_str = |s: State<'a, T>| match s.data {
129 Data::Value(v) => v.as_str().map(|s| s.to_string()),
130 Data::Ref(Pointer { inner, .. }) => inner.as_str().map(|s| s.to_string()),
131 _ => None,
132 };
133
134 match (to_str(lhs), to_str(rhs)) {
135 (Some(lhs), Some(rhs)) => Regex::new(&prepare_regex(rhs, substr))
136 .map(|re| to_state(regex(&lhs, re)))
137 .unwrap_or(to_state(false)),
138 _ => to_state(false),
139 }
140}
141
142fn prepare_regex(pattern: String, substring: bool) -> String {
148 let mut translated = String::with_capacity(pattern.len());
149 let mut chars = pattern.chars();
150 let mut in_class = false;
151 while let Some(c) = chars.next() {
152 match c {
153 '\\' => {
154 translated.push(c);
155 if let Some(escaped) = chars.next() {
156 translated.push(escaped);
157 }
158 }
159 '[' if !in_class => {
160 in_class = true;
161 translated.push(c);
162 }
163 ']' if in_class => {
164 in_class = false;
165 translated.push(c);
166 }
167 '.' if !in_class => translated.push_str(r"[^\n\r]"),
168 _ => translated.push(c),
169 }
170 }
171
172 if substring {
173 translated
174 } else {
175 format!("^(?:{translated})$")
176 }
177}
178
179fn value<T: Queryable>(state: State<T>) -> State<T> {
180 match state.data {
181 Data::Ref(..) | Data::Value(..) => state,
182 Data::Refs(items) if items.len() == 1 => {
183 State::data(state.root, Data::Ref(items[0].clone()))
184 }
185 _ => State::nothing(state.root),
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use crate::parser::model::Segment;
192 use crate::parser::model::Selector;
193 use crate::parser::model::Test;
194 use crate::parser::model::TestFunction;
195 use crate::query::state::{Data, Pointer, State};
196 use crate::query::test_function::{regex, FnArg};
197 use crate::query::Query;
198 use crate::{arg, q_segment, segment, selector, test, test_fn};
199 use serde_json::json;
200
201 #[test]
202 fn test_len() {
203 let json = json!({"array": [1,2,3]});
204 let state = State::root(&json);
205
206 let query = test_fn!(length arg!(t test!(@ segment!(selector!(array)))));
207 let res = query.process(state);
208
209 assert_eq!(res.ok_val(), Some(json!(3)));
210 }
211
212 #[test]
213 fn test_match_1() {
214 let json = json!({"a": "abc sdgfudsf","b": "abc.*"});
215 let state = State::root(&json);
216
217 let query = test_fn!(match
218 arg!(t test!(@ segment!(selector!(a)))),
219 arg!(t test!(@ segment!(selector!(b))))
220 );
221 let res = query.process(state);
222
223 assert_eq!(res.ok_val(), Some(json!(true)));
224 }
225
226 #[test]
227 fn test_count_1() {
228 let json = json!({"array": [1,2,3]});
229 let state = State::root(&json);
230
231 let query = test_fn!(count arg!(t test!(@ segment!(selector!(array)))));
232 let res = query.process(state);
233
234 assert_eq!(res.ok_val(), Some(json!(1)));
235 }
236
237 #[test]
238 fn test_search() {
239 let json = json!("123");
240 let state = State::root(&json);
241 let reg = State::str("[a-z]+", &json);
242
243 let res = regex(state, reg, true);
244
245 assert_eq!(res.ok_val(), Some(json!(false)));
246 }
247
248 #[test]
249 fn test_match() {
250 let json = json!("bbab");
251 let state = State::root(&json);
252 let reg = State::str("^b.?b$", &json);
253
254 let res = regex(state, reg, false);
255
256 assert_eq!(res.ok_val(), Some(json!(false)));
257 }
258}