1use crate::error::ToqlError;
17use crate::query::concatenation::Concatenation;
18use crate::query::field::Field;
19use crate::query::field_filter::FieldFilter;
20use crate::query::field_order::FieldOrder;
21use crate::query::predicate::Predicate;
22use crate::query::query_token::QueryToken;
23use crate::query::selection::Selection;
24use crate::query::wildcard::Wildcard;
25use crate::query::Query;
26use crate::sql_arg::SqlArg;
27use crate::sql_builder::sql_builder_error::SqlBuilderError;
28use toql_query_parser::PestQueryParser;
29
30use pest::Parser;
31
32use toql_query_parser::Rule;
33
34pub struct QueryParser;
37
38enum TokenType {
39 Field,
40 Wildcard,
41 Predicate,
42 Selection,
43 Unknown,
44}
45
46struct TokenInfo {
47 token_type: TokenType,
48 args: Vec<SqlArg>,
49 hidden: bool,
50 order: Option<FieldOrder>,
51 filter: Option<String>,
52 name: String,
54 concatenation: Concatenation,
55}
56
57impl TokenInfo {
58 pub fn new() -> Self {
59 TokenInfo {
60 token_type: TokenType::Unknown,
61 args: Vec::new(),
62 hidden: false,
63 order: None,
64 filter: None,
65 name: String::new(),
67 concatenation: Concatenation::And,
68 }
69 }
70 pub fn build_token(&mut self) -> Result<Option<QueryToken>, ToqlError> {
71 match &self.token_type {
72 TokenType::Field => Ok(Some(QueryToken::Field(Field {
73 name: self.name.to_string(),
74 hidden: self.hidden,
75 order: self.order.clone(),
76 filter: self.build_filter()?,
77 concatenation: self.concatenation.clone(),
79 }))),
80 TokenType::Wildcard => Ok(Some(QueryToken::Wildcard(Wildcard {
81 path: self.name.to_string(),
82 concatenation: self.concatenation.clone(),
83 }))),
84 TokenType::Predicate => Ok(Some(QueryToken::Predicate(Predicate {
85 name: self.name.to_string(),
86 args: self.args.drain(..).collect(),
87 concatenation: self.concatenation.clone(),
88 }))),
89 TokenType::Selection => {
90 Ok(Some(QueryToken::Selection(Selection {
93 name: String::from(if self.name.is_empty() {
94 "std"
95 } else {
96 self.name.as_str()
97 }),
98 concatenation: self.concatenation.clone(),
99 })))
100 }
101 _ => Ok(None),
102 }
103 }
104
105 pub fn build_filter(&mut self) -> Result<Option<FieldFilter>, ToqlError> {
106 match &self.filter {
107 Some(f) => {
108 let upc = f.to_uppercase();
109 let filtername = upc.split_ascii_whitespace().next().unwrap_or("");
110 match filtername {
111 "" => Ok(None),
112 "EQ" => Ok(Some(FieldFilter::Eq(self.args.pop().ok_or_else(|| {
113 SqlBuilderError::FilterInvalid(filtername.to_string())
114 })?))),
115 "EQN" => Ok(Some(FieldFilter::Eqn)),
116 "NE" => Ok(Some(FieldFilter::Ne(self.args.pop().ok_or_else(|| {
117 SqlBuilderError::FilterInvalid(filtername.to_string())
118 })?))),
119 "NEN" => Ok(Some(FieldFilter::Nen)),
120 "GT" => Ok(Some(FieldFilter::Gt(self.args.pop().ok_or_else(|| {
121 SqlBuilderError::FilterInvalid(filtername.to_string())
122 })?))),
123 "GE" => Ok(Some(FieldFilter::Ge(self.args.pop().ok_or_else(|| {
124 SqlBuilderError::FilterInvalid(filtername.to_string())
125 })?))),
126 "LT" => Ok(Some(FieldFilter::Lt(self.args.pop().ok_or_else(|| {
127 SqlBuilderError::FilterInvalid(filtername.to_string())
128 })?))),
129 "LE" => Ok(Some(FieldFilter::Le(self.args.pop().ok_or_else(|| {
130 SqlBuilderError::FilterInvalid(filtername.to_string())
131 })?))),
132 "LK" => Ok(Some(FieldFilter::Lk(self.args.pop().ok_or_else(|| {
133 SqlBuilderError::FilterInvalid(filtername.to_string())
134 })?))),
135 "IN" => Ok(Some(FieldFilter::In(self.args.drain(..).collect()))),
136 "OUT" => Ok(Some(FieldFilter::Out(self.args.drain(..).collect()))),
137 "BW" => {
138 let to = self.args.pop().ok_or_else(|| {
139 SqlBuilderError::FilterInvalid(filtername.to_string())
140 })?;
141 let from = self.args.pop().ok_or_else(|| {
142 SqlBuilderError::FilterInvalid(filtername.to_string())
143 })?;
144 Ok(Some(FieldFilter::Bw(from, to)))
145 }
146 _ => {
147 if upc.starts_with("FN ") {
148 let filtername = upc.trim_start_matches("FN ");
149 Ok(Some(FieldFilter::Fn(
150 filtername.to_string(),
151 self.args.drain(..).collect(),
152 )))
153 } else {
154 Err(SqlBuilderError::FilterInvalid(f.to_string()).into())
155 }
156 }
157 }
158 }
159 None => Ok(None),
160 }
161 }
162}
163
164impl QueryParser {
165 pub fn parse<M>(toql_query: &str) -> Result<Query<M>, ToqlError> {
169 let pairs = PestQueryParser::parse(Rule::query, toql_query)?;
170
171 let mut query = Query::new();
172
173 let mut token_info = TokenInfo::new();
174
175 for pair in pairs.flatten().into_iter() {
176 let span = pair.clone().as_span();
177 match pair.as_rule() {
178 Rule::field_clause => {
179 token_info.token_type = TokenType::Field;
180 }
181 Rule::sort => {
182 let p = span.as_str()[1..].parse::<u8>().unwrap_or(1);
183 if let Some('+') = span.as_str().chars().next() {
184 token_info.order = Some(FieldOrder::Asc(p));
185 } else {
186 token_info.order = Some(FieldOrder::Desc(p));
187 }
188 }
189 Rule::hidden => {
190 token_info.hidden = true;
191 }
192
193 Rule::field_path => {
194 token_info.name = span.as_str().to_string();
195 }
196 Rule::wildcard => {
197 token_info.name = span.as_str().trim_end_matches('*').to_string();
198 if !token_info.name.is_empty() && !token_info.name.ends_with('_') {
200 token_info.name.push('_');
201 }
202 token_info.token_type = TokenType::Wildcard;
203 }
204 Rule::filter0_name => {
205 token_info.filter = Some(span.as_str().to_string());
206 }
207 Rule::filter1_name => {
208 token_info.filter = Some(span.as_str().to_string());
209 }
210 Rule::filter2_name => {
211 token_info.filter = Some(span.as_str().to_string());
212 }
213 Rule::filterx_name => {
214 token_info.filter = Some(span.as_str().to_string());
215 }
216 Rule::filterc_name => {
217 token_info.filter = Some(span.as_str().to_string());
218 }
219 Rule::num_u64 => {
220 let v = span.as_str().parse::<u64>().unwrap_or(0); token_info.args.push(SqlArg::from(v));
222 }
223 Rule::num_i64 => {
224 let v = span.as_str().parse::<i64>().unwrap_or(0); token_info.args.push(SqlArg::from(v));
226 }
227 Rule::num_f64 => {
228 let v = span.as_str().parse::<f64>().unwrap_or(0.0); token_info.args.push(SqlArg::from(v));
230 }
231 Rule::string => {
232 let v = span
233 .as_str()
234 .trim_start_matches('\'')
235 .trim_end_matches('\'')
236 .replace("''", "'");
237 token_info.args.push(SqlArg::from(v));
238 }
239 Rule::predicate_clause => {
240 token_info.token_type = TokenType::Predicate;
241 }
242 Rule::predicate_name => {
243 token_info.name = span.as_str().trim_start_matches('@').to_string();
244 }
245 Rule::selection_clause => {
246 token_info.token_type = TokenType::Selection;
247 }
248 Rule::selection_name => {
249 token_info.name = span.as_str().trim_start_matches('@').to_string();
250 }
251 Rule::rpar => {
252 if let Some(token) = token_info.build_token()? {
255 query.tokens.push(token);
256 token_info = TokenInfo::new(); }
258 query.tokens.push(QueryToken::RightBracket);
259 }
260 Rule::lpar => {
261 query
262 .tokens
263 .push(QueryToken::LeftBracket(token_info.concatenation.clone()));
264 }
265 Rule::separator => {
266 let concat_type = span.as_str().chars().next();
267 if let Some(token) = token_info.build_token()? {
268 query.tokens.push(token);
269 token_info = TokenInfo::new(); }
271
272 token_info.concatenation = if let Some(',') = concat_type {
273 Concatenation::And
274 } else {
275 Concatenation::Or
276 };
277 }
278
279 _ => {}
280 }
281 }
282 if let Some(token) = token_info.build_token()?
283 {
285 query.tokens.push(token);
286 }
287 Ok(query)
288 }
289}
290
291#[cfg(test)]
292mod test {
293 use super::QueryParser;
294
295 struct User;
296
297 #[test]
298 fn parse_filters() {
299 let q = QueryParser::parse::<User>(
300 "prop1, prop2 EQN, prop3 NE -1, prop4 NEN, \
301 prop5 GT 1.5, prop6 GE 1.5, prop7 LT 1.5, prop8 LE 1.5",
302 )
303 .unwrap();
304
305 assert_eq!(
306 q.to_string(),
307 "prop1,prop2 EQN,prop3 NE -1,prop4 NEN,\
308 prop5 GT 1.5,prop6 GE 1.5,prop7 LT 1.5,prop8 LE 1.5"
309 );
310
311 let q = QueryParser::parse::<User>(
312 "prop9 LK 'ABC', prop10 BW 1 10, prop11 IN 1 2 3, prop12 OUT 1 2 3, \
313 prop13 FN CUSTOM 'A' 'B' 2",
314 )
315 .unwrap();
316
317 assert_eq!(
318 q.to_string(),
319 "prop9 LK 'ABC',prop10 BW 1 10,prop11 IN 1 2 3,prop12 OUT 1 2 3,\
320 prop13 FN CUSTOM 'A' 'B' 2"
321 );
322 }
323 #[test]
324 fn parse_parens() {
325 let q = QueryParser::parse::<User>("(prop1 eq 1, prop2 eqn); prop3 ne 1").unwrap();
326 assert_eq!(q.to_string(), "(prop1 EQ 1,prop2 EQN);prop3 NE 1");
327
328 let q = QueryParser::parse::<User>("(((prop1 eq 1, prop2 eqn)); prop3 ne 1)").unwrap();
329 assert_eq!(q.to_string(), "(((prop1 EQ 1,prop2 EQN));prop3 NE 1)");
330 }
331 #[test]
332 fn parse_predicate() {
333 let q = QueryParser::parse::<User>("@level1_pred, @pred").unwrap();
334 assert_eq!(q.to_string(), "@level1_pred,@pred");
335 }
336 #[test]
337 fn parse_selection() {
338 let q = QueryParser::parse::<User>("$level1_mut, $mut").unwrap();
339 assert_eq!(q.to_string(), "$level1_mut,$mut");
340 }
341 #[test]
342 fn parse_wildcard_and_field() {
343 let q = QueryParser::parse::<User>("level1_*,*, b").unwrap();
344 assert_eq!(q.to_string(), "level1_*,*,b");
345 }
346 #[test]
347 fn parse_order_and_hidden() {
348 let q = QueryParser::parse::<User>("+1level1_a, -2.b").unwrap();
349 assert_eq!(q.to_string(), "+1level1_a,-2.b");
350 }
351}