1use crate::{BoolTagExprLexicalParse, ParseError, Tag, Token};
6use serde::{Deserialize, Deserializer, Serialize, Serializer};
7use thiserror::Error;
8
9#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
13pub struct BoolTagExpr(Node);
14
15#[derive(Debug, Clone, PartialEq, Eq, Hash, Error)]
17pub enum SqlTableInfoError {
18 #[error("Invalid identifiers: '{0}'")]
20 InvalidIdentifier(String),
21}
22
23#[derive(Debug, Clone, PartialEq, Eq, Hash)]
27pub struct DbTableInfo {
28 table_name: String,
31
32 id_column: String,
35
36 tag_name_column: String,
38
39 tag_value_column: String,
41}
42
43impl DbTableInfo {
44 pub fn from(
47 table_name: &str,
48 id_column: &str,
49 tag_name_column: &str,
50 tag_value_column: &str,
51 ) -> Result<Self, SqlTableInfoError> {
52 for identifier in [table_name, id_column, tag_name_column, tag_value_column] {
53 if !is_valid_sql_identifier(identifier) {
54 Err(SqlTableInfoError::InvalidIdentifier(identifier.to_string()))?;
55 }
56 }
57
58 Ok(Self {
59 table_name: table_name.to_string(),
60 id_column: id_column.to_string(),
61 tag_name_column: tag_name_column.to_string(),
62 tag_value_column: tag_value_column.to_string(),
63 })
64 }
65}
66
67fn is_valid_sql_identifier(s: &str) -> bool {
71 let mut chars = s.chars();
72 match chars.next() {
73 Some(c) if c.is_ascii_alphabetic() || c == '_' => {
75 chars.all(|ch| ch.is_ascii_alphanumeric() || ch == '_')
77 }
78 _ => false,
79 }
80}
81
82impl Serialize for BoolTagExpr {
84 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
85 where
86 S: Serializer,
87 {
88 let bool_expr = self.clone().to_boolean_expression();
89 serializer.serialize_str(&bool_expr)
90 }
91}
92
93impl<'de> Deserialize<'de> for BoolTagExpr {
95 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
96 where
97 D: Deserializer<'de>,
98 {
99 let raw_expr = String::deserialize(deserializer)?;
100 let tree = Self::from(raw_expr);
101 match tree {
102 Ok(tree) => Ok(tree),
103 Err(error) => {
104 let err_msg = format!("Boolean expressions is invalid: {error}");
106 Err(serde::de::Error::custom(err_msg))
107 }
108 }
109 }
110}
111
112#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
115pub enum Node {
116 And(Box<Node>, Box<Node>),
117 Or(Box<Node>, Box<Node>),
118 Not(Box<Node>),
119 Tag(Tag),
120 Bool(bool),
121}
122
123#[derive(Debug, PartialEq, Clone, Error, Hash, Eq)]
125pub enum SyntaxParseError {
126 #[error("No tags in expression")]
128 NoTags,
129
130 #[error("Invalid opening token")]
133 InvalidOpeningToken,
134
135 #[error("Invalid closing token")]
138 InvalidClosingToken,
139
140 #[error("Unopended brackets")]
144 UnopenedBrackets,
145
146 #[error("Unclosed brackets")]
150 UnclosedBrackets,
151
152 #[error("Invalid sequence of tokens: {0} -> {1}")]
154 InvalidSequence(Token, Token),
155}
156
157pub trait BoolTagExprSyntaxParse<T: BoolTagExprLexicalParse> {
160 fn syntax_parse(self) -> Result<BoolTagExpr, ParseError>;
163}
164
165impl<T: BoolTagExprLexicalParse> BoolTagExprSyntaxParse<T> for T {
169 fn syntax_parse(self) -> Result<BoolTagExpr, ParseError> {
170 let lexical_tokens = self.lexical_parse()?;
171 validate_token_stream(lexical_tokens.tokens().to_owned())?;
172 Ok(BoolTagExpr(syntax_parse_token_stream(
173 &mut lexical_tokens.tokens().to_owned(),
174 )))
175 }
176}
177
178impl BoolTagExpr {
179 pub fn from<T>(boolean_expr: T) -> Result<Self, ParseError>
182 where
183 T: BoolTagExprLexicalParse + BoolTagExprSyntaxParse<T>,
184 {
185 boolean_expr.syntax_parse()
186 }
187
188 #[must_use]
190 pub fn to_boolean_expression(self) -> String {
191 boolean_expr_tree_to_logical_expr_string(self.0)
192 }
193
194 #[must_use]
207 pub fn to_sql(self, table_info: &DbTableInfo) -> String {
208 boolean_expr_tree_to_sql(self.0, table_info)
209 }
210
211 #[must_use]
213 pub fn into_node(self) -> Node {
214 self.0
215 }
216}
217
218fn boolean_expr_tree_to_sql(expr: Node, table_info: &DbTableInfo) -> String {
226 match expr {
227 Node::And(l, r) => {
228 let mut sql_fragment = format!(
229 "SELECT {} FROM {} WHERE {} IN (",
230 table_info.id_column, table_info.table_name, table_info.id_column
231 );
232 sql_fragment.push_str(&boolean_expr_tree_to_sql(*l, table_info));
233 sql_fragment.push_str(&format!(") AND {} IN (", table_info.id_column));
234 sql_fragment.push_str(&boolean_expr_tree_to_sql(*r, table_info));
235 sql_fragment.push_str(&format!(") GROUP BY {}", table_info.id_column));
236 sql_fragment
237 }
238 Node::Or(l, r) => {
239 let mut sql_fragment = format!("SELECT {} FROM (", table_info.id_column);
240 sql_fragment.push_str(&boolean_expr_tree_to_sql(*l, table_info));
241 sql_fragment.push_str(" UNION ");
242 sql_fragment.push_str(&boolean_expr_tree_to_sql(*r, table_info));
243 sql_fragment.push(')');
244 sql_fragment
245 }
246 Node::Not(e) => {
247 let mut sql_fragment = format!(
248 "SELECT {} FROM {} WHERE {} NOT IN (",
249 table_info.id_column, table_info.table_name, table_info.id_column
250 );
251 sql_fragment.push_str(&boolean_expr_tree_to_sql(*e, table_info));
252 sql_fragment.push(')');
253 sql_fragment
254 }
255 Node::Tag(tag) => match tag.name {
256 None => format!(
257 "SELECT {} FROM {} WHERE {}='{}'",
258 table_info.id_column, table_info.table_name, table_info.tag_value_column, tag.value
259 ),
260 Some(tag_name) => format!(
261 "SELECT {} FROM {} WHERE {}='{}' AND {}='{}'",
262 table_info.id_column,
263 table_info.table_name,
264 table_info.tag_name_column,
265 tag_name,
266 table_info.tag_value_column,
267 tag.value
268 ),
269 },
270 Node::Bool(_) => panic!(),
271 }
272}
273
274fn boolean_expr_tree_to_logical_expr_string(expr: Node) -> String {
277 match expr {
278 Node::And(l, r) => {
279 let mut sql_fragment = String::from("(");
280 sql_fragment.push_str(&boolean_expr_tree_to_logical_expr_string(*l));
281 sql_fragment.push_str(" & ");
282 sql_fragment.push_str(&boolean_expr_tree_to_logical_expr_string(*r));
283 sql_fragment.push(')');
284 sql_fragment
285 }
286 Node::Or(l, r) => {
287 let mut sql_fragment = String::from("(");
288 sql_fragment.push_str(&boolean_expr_tree_to_logical_expr_string(*l));
289 sql_fragment.push_str(" | ");
290 sql_fragment.push_str(&boolean_expr_tree_to_logical_expr_string(*r));
291 sql_fragment.push(')');
292 sql_fragment
293 }
294 Node::Not(e) => {
295 let mut sql_fragment = String::from("!");
296 sql_fragment.push_str(&boolean_expr_tree_to_logical_expr_string(*e));
297 sql_fragment
298 }
299 Node::Tag(tag) => match tag.name {
300 None => format!("{}", tag.value),
301 Some(tag_name) => format!("({}={})", tag_name, tag.value),
302 },
303 Node::Bool(_) => panic!(),
304 }
305}
306
307fn validate_token_stream(mut tokens: Vec<Token>) -> Result<(), SyntaxParseError> {
311 let mut at_least_1_tag = false;
312 for token in tokens.clone() {
313 if let Token::Tag(_) = token {
314 at_least_1_tag = true;
315 break;
316 }
317 }
318 if !at_least_1_tag {
319 return Err(SyntaxParseError::NoTags);
320 }
321
322 let mut opening_bracket_count = 0;
323 let mut closing_bracket_count = 0;
324
325 let mut previous_token = tokens.remove(0);
326
327 match &previous_token {
329 Token::Not | Token::OpenBracket | Token::Tag(_) => Ok(()),
330 _ => return Err(SyntaxParseError::InvalidOpeningToken),
331 }?;
332
333 for token in tokens {
334 match previous_token {
335 Token::OpenBracket => {
336 opening_bracket_count += 1;
337 match token.clone() {
338 Token::OpenBracket | Token::Not | Token::Tag(_) => Ok(()),
340
341 Token::CloseBracket => {
343 return Err(SyntaxParseError::InvalidSequence(
344 Token::OpenBracket,
345 Token::CloseBracket,
346 ))
347 }
348 Token::And => {
349 return Err(SyntaxParseError::InvalidSequence(
350 Token::OpenBracket,
351 Token::And,
352 ))
353 }
354 Token::Or => {
355 return Err(SyntaxParseError::InvalidSequence(
356 Token::OpenBracket,
357 Token::Or,
358 ))
359 }
360 }
361 }
362 Token::CloseBracket => {
363 closing_bracket_count += 1;
364 match token.clone() {
365 Token::CloseBracket | Token::And | Token::Or => Ok(()),
367
368 Token::OpenBracket => {
370 return Err(SyntaxParseError::InvalidSequence(
371 Token::CloseBracket,
372 Token::OpenBracket,
373 ))
374 }
375 Token::Not => {
376 return Err(SyntaxParseError::InvalidSequence(
377 Token::CloseBracket,
378 Token::Not,
379 ))
380 }
381 Token::Tag(tag) => {
382 return Err(SyntaxParseError::InvalidSequence(
383 Token::CloseBracket,
384 Token::Tag(tag),
385 ))
386 }
387 }
388 }
389 Token::Not => match token.clone() {
390 Token::Tag(_) | Token::OpenBracket => Ok(()),
392
393 Token::CloseBracket => {
395 return Err(SyntaxParseError::InvalidSequence(
396 Token::Not,
397 Token::CloseBracket,
398 ))
399 }
400 Token::Not => {
401 return Err(SyntaxParseError::InvalidSequence(Token::Not, Token::Not))
402 }
403 Token::And => {
404 return Err(SyntaxParseError::InvalidSequence(Token::Not, Token::And))
405 }
406 Token::Or => return Err(SyntaxParseError::InvalidSequence(Token::Not, Token::Or)),
407 },
408 Token::And => match token.clone() {
409 Token::Not | Token::OpenBracket | Token::Tag(_) => Ok(()),
411
412 Token::CloseBracket => {
414 return Err(SyntaxParseError::InvalidSequence(
415 Token::And,
416 Token::CloseBracket,
417 ))
418 }
419 Token::And => {
420 return Err(SyntaxParseError::InvalidSequence(Token::And, Token::And))
421 }
422 Token::Or => return Err(SyntaxParseError::InvalidSequence(Token::And, Token::Or)),
423 },
424 Token::Or => match token.clone() {
425 Token::Not | Token::OpenBracket | Token::Tag(_) => Ok(()),
427
428 Token::CloseBracket => {
430 return Err(SyntaxParseError::InvalidSequence(
431 Token::Or,
432 Token::CloseBracket,
433 ))
434 }
435 Token::And => return Err(SyntaxParseError::InvalidSequence(Token::Or, Token::And)),
436 Token::Or => return Err(SyntaxParseError::InvalidSequence(Token::Or, Token::Or)),
437 },
438 Token::Tag(previous_tag) => match token.clone() {
439 Token::CloseBracket | Token::And | Token::Or => Ok(()),
441
442 Token::OpenBracket => {
444 return Err(SyntaxParseError::InvalidSequence(
445 Token::Tag(previous_tag),
446 Token::OpenBracket,
447 ))
448 }
449 Token::Not => {
450 return Err(SyntaxParseError::InvalidSequence(
451 Token::Tag(previous_tag),
452 Token::Not,
453 ))
454 }
455 Token::Tag(this_tag) => {
456 println!("{previous_tag} then {this_tag}, is not allowed");
457 return Err(SyntaxParseError::InvalidSequence(
458 Token::Tag(previous_tag),
459 Token::Tag(this_tag),
460 ));
461 }
462 },
463 }?;
464
465 previous_token = token;
466 }
467
468 match &previous_token {
470 Token::CloseBracket => {
471 closing_bracket_count += 1;
472 Ok(())
473 }
474 Token::Tag(_) => Ok(()),
475 _ => return Err(SyntaxParseError::InvalidClosingToken),
476 }?;
477
478 if closing_bracket_count > opening_bracket_count {
479 return Err(SyntaxParseError::UnopenedBrackets);
480 }
481
482 if closing_bracket_count < opening_bracket_count {
483 return Err(SyntaxParseError::UnclosedBrackets);
484 }
485
486 Ok(())
487}
488
489fn syntax_parse_token_stream(tokens: &mut Vec<Token>) -> Node {
492 let mut expr: Node = recursive_syntax_parse(tokens, None);
493 loop {
494 if tokens.is_empty() {
495 break;
496 }
497 expr = recursive_syntax_parse(tokens, Some(expr));
498 }
499 expr
500}
501
502fn recursive_syntax_parse(tokens: &mut Vec<Token>, expr: Option<Node>) -> Node {
505 if tokens.is_empty() {
506 return expr.unwrap();
507 }
508 let token = tokens.remove(0);
509 match token {
510 Token::OpenBracket => {
511 recursive_syntax_parse(tokens, expr)
513 }
514 Token::CloseBracket => expr.unwrap(),
515 Token::And => {
516 let result = recursive_syntax_parse(tokens, None);
517 Node::And(Box::new(expr.unwrap()), Box::new(result))
518 }
519 Token::Or => {
520 let result = recursive_syntax_parse(tokens, None);
521 Node::Or(Box::new(expr.unwrap()), Box::new(result))
522 }
523 Token::Tag(tag) => recursive_syntax_parse(tokens, Some(Node::Tag(tag))),
524 Token::Not => {
525 let result = recursive_syntax_parse(tokens, None);
526 Node::Not(Box::new(result))
527 }
528 }
529}
530
531#[cfg(test)]
532mod test {
533 use super::*;
534
535 #[test]
536 fn syntax_parse_empty() {
537 let a = "";
539 assert!(a.syntax_parse().err().unwrap() == ParseError::Syntax(SyntaxParseError::NoTags));
540
541 let a = "(&)";
542 assert!(a.syntax_parse().err().unwrap() == ParseError::Syntax(SyntaxParseError::NoTags));
543
544 let a = "(& & &) | (& & &)";
545 assert!(a.syntax_parse().err().unwrap() == ParseError::Syntax(SyntaxParseError::NoTags));
546 }
547
548 #[test]
549 fn syntax_parse() {
550 let a = "((nationality=american && scientist) || (=british & scientist)) & !man && person";
552 assert!(a.syntax_parse().is_err());
553
554 let a = "((nationality=american & scientist) | (=british & scientist)) && !man & person";
556 assert!(a.syntax_parse().is_err());
557
558 let a = "((nationality=american & scientist) || (=british & scientist)) & !man && person";
560 assert!(a.syntax_parse().is_err());
561
562 let a = "((nationality=american & scientist) | (=british & scientist)) & !man & person";
564 assert!(a.syntax_parse().is_ok());
565
566 let a = "(a & b";
568 assert!(a.syntax_parse().is_err());
569
570 let a = "(a & b & c)";
572 assert!(a.syntax_parse().is_ok());
573 }
574
575 #[test]
583 fn to_sql() {
584 let table_info = DbTableInfo::from(
585 &"table_name",
586 &"id_column",
587 &"tag_name_column",
588 &"tag_value_column",
589 )
590 .unwrap();
591
592 let a = "((x=a & b) | (c & b)) & !d";
594 assert!(a.syntax_parse().unwrap().to_sql(&table_info).is_ascii());
595 }
596
597 #[test]
599 fn to_boolean_expression() {
600 let a = "((x=a & b) | (c & b)) & !d";
602 let parsed = a.syntax_parse().unwrap().to_boolean_expression();
603 let parsed_again = parsed
604 .clone()
605 .syntax_parse()
606 .unwrap()
607 .to_boolean_expression();
608 assert_eq!(parsed, parsed_again);
609 }
610}