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 {
143 let pattern = if !substring {
144 let pattern = if pattern.starts_with('^') {
145 pattern
146 } else {
147 format!("^{}", pattern)
148 };
149 let pattern = if pattern.ends_with('$') {
150 pattern
151 } else {
152 format!("{}$", pattern)
153 };
154 pattern
155 } else {
156 pattern.to_string()
157 };
158 let pattern = if pattern.contains("\\\\") {
159 pattern.replace("\\\\", "\\")
160 } else {
161 pattern.to_string()
162 };
163
164 pattern.trim_matches(|c| c == '\'' || c == '"').to_string()
165}
166
167fn value<T: Queryable>(state: State<T>) -> State<T> {
168 match state.data {
169 Data::Ref(..) | Data::Value(..) => state,
170 Data::Refs(items) 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)]
178mod 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}