1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
//!
//! # About
//!
//! This crate allows boolean expressions of tags to be written,
//! parsed, and evaluated (e.g. converted to SQL for selecting out of a
//! database). The tags can be restricted to the more typical single-value
//! kind, but support is also supplied for key-value tags - this is achieved by
//! making the tag name optional.
//!
//! For example, to get all British and American scientists who are not
//! chemists (see below for a proper example in Rust) one could use:
//!
//! `((nationality=american & scientist) | (=british & scientist)) & !chemist & person`
//!
//! The input requirements aren't rigid and so to get the same result we could
//! also write, say:
//!
//! `(nationality=american | =british) & scientist & !chemist & person`
//!
//! Where:
//!
//! - `nationality=american` means entities with the a tag whose name is
//! `nationality` and whose value is `american`
//! - `=british` is the same as `british` and means entities with a tag value of
//! `british`
//! - `!chemist` means entities that do not have the tag value `chemist`
//! - `person` means those entities that have a tag value of `person`
//!
//! ## Syntax
//!
//! - `&` for `AND` (`&&` is a lexical error)
//! - `|` for `OR` (`||` is a lexical error)
//! - `(` and `)` for logical grouping (`)(` is a syntax error)
//! - `!` for `NOT`
//! - `tag-name=tag-value`, `=tag-value`, and `tag-value` are all valid tags
//!
//! # Parsing
//!
//! A valid boolean expression must contain at least 1 tag.
//!
//! This modules provides functionality for both:
//!
//! 1. String (or custom type with necessary trait) → Bool expression tree
//! 2. String (or custom type with necessary trait) → Lexical token stream → Bool expression tree
//!
//! See below for examples and clarification.
//!
//! # Examples
//!
//! ## Basic
//!
//! The simplest way to use this crate is to parse some boolean tag expression
//! string and to then select items out of a database. Here is how to do that:
//!
//! ```rust
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use bool_tag_expr::BoolTagExpr;
//! use bool_tag_expr::DbTableInfo;
//!
//! // The boolean tag expression
//! let expr_str = "(nationality=american | =british) & scientist & !chemist & person";
//!
//! // Parse it, returning early if there is an error
//! let expr_tree = BoolTagExpr::from(expr_str)?;
//!
//! // Setup the database info
//! let table_name = "entity_tags";
//! let id_column = "entity_id";
//! let tag_name_column = "name";
//! let tag_value_column = "value";
//! let table_info = DbTableInfo::from(table_name, id_column, tag_name_column, tag_value_column)?;
//!
//! // Generate the SQL using the expression tree and the database info
//! let bool_expr_sql_str = expr_tree.to_sql(&table_info);
//!
//! // Create the full SQL statement
//! let sql = format!(
//! r#"
//! SELECT DISTINCT {id_column}
//! FROM ({bool_expr_sql_str})
//! LIMIT ?
//! "#
//! );
//!
//! # Ok(())
//! # }
//! ```
//!
//! ## Advanced
//!
//! Should you want to get a [`BoolTagExpr`] from some type that isn't readily
//! converted into a string, you can implement [`BoolTagExprLexicalParse`] for
//! it. You will then automatically get an implementation of
//! [`BoolTagExprSyntaxParse`]. You can then use `BoolTagExpr::from(my_var)?;`
//! as above. Alternatively, you can lexically parse and then syntactically
//! parse any type implementing these 2 traits (already implemented for
//! strings). This might be helpful if, for example, you want to limit the
//! number of tags in a boolean expression:
//!
//! ```
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use bool_tag_expr::BoolTagExprSyntaxParse;
//! use bool_tag_expr::BoolTagExprLexicalParse;
//! use bool_tag_expr::Token;
//!
//! // Get the boolean expression (e.g. from user input)
//! let expr = "(german | british) & poet";
//!
//! // Parse (lexical only) the boolean expression
//! let tokens = expr.lexical_parse().unwrap_or_else(|error| {
//! eprintln!("Lexical parse error: {error}");
//! std::process::exit(1);
//! });
//!
//! // Count the number of tags
//! let tag_count = tokens
//! .tokens()
//! .iter()
//! .filter(|token| if let Token::Tag(_) = token { true } else { false })
//! .count();
//!
//! // Panic if there are too many tags
//! if tag_count > 5 {
//! panic!()
//! }
//!
//! // Parse (syntax) the tokens
//! let expr_tree = expr.syntax_parse().unwrap_or_else(|error| {
//! eprintln!("Syntax parse error: {error}");
//! std::process::exit(1);
//! });
//!
//! # Ok(())
//! # }
//! ```
//!
//! # Crate Features
//!
//! There is only 1 optional feature: `sqlx`. It is not selected by default.
//! Unless you are planning on compiling parts of the crate to WASM, it is
//! recommended that you use the `sqlx` feature. If you are planning on pulling
//! data out of a database, you must use this feature.
//!
//! # Warnings
//!
//! For the `NOT` functionality to work correctly for all entires, every entry
//! must have at least 1 tag.
//!
pub use *;
pub use *;
pub use *;
use Error;
/// All possible lexical and syntactic parsing errors