2 releases
| 0.1.0-beta.2 | Mar 12, 2026 |
|---|---|
| 0.1.0-beta.1 | Dec 3, 2025 |
#11 in #bool
Used in 7 crates
48KB
859 lines
bool-tag-expr
This crate is still in beta release
Overview
This is the repo for the bool-tag-expr rust crate, which was created to improve fetching & filtering things by their tags. It facilitates parsing boolean expressions of tags into boolean expression trees that can be either transformed into SQL for selecting from a database, or evaluated against a list of entities to filter them.
Example
To select all British and French scientists who are not biologists, one can write (british | french) & scientist & !biologist. Using the crate this can be transformed into the SQL needed to select matching entries out of a database. For complete & working examples please visit docs.rs.
lib.rs:
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=americanmeans entities with the a tag whose name isnationalityand whose value isamerican=britishis the same asbritishand means entities with a tag value ofbritish!chemistmeans entities that do not have the tag valuechemistpersonmeans those entities that have a tag value ofperson
Syntax
&forAND(&&is a lexical error)|forOR(||is a lexical error)(and)for logical grouping ()(is a syntax error)!forNOTtag-name=tag-value,=tag-value, andtag-valueare all valid tags
Parsing
A valid boolean expression must contain at least 1 tag.
This modules provides functionality for both:
- String (or custom type with necessary trait) → Bool expression tree
- 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:
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 ?
"#
);
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:
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);
});
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.
Dependencies
~1–4MB
~57K SLoC