Crate bool_tag_expr

Crate bool_tag_expr 

Source
Expand description

§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:

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.

Structs§

BoolTagExpr
A boolean expression tree.
DbTableInfo
Holds information about the table against which the SQL will be run - this is used to produce the SQL.
LexicalTokenStream
A stream of lexical Tokens
Tag
A Tag may optionally have a name, but must have a value
TagComponent
Represents both tag names and values (see TagName & TagValue).

Enums§

LexicalParseError
Possible lexical errors
Node
Possible elements of a boolean expression (a boolean expression tree is made up of these)
ParseError
All possible lexical and syntactic parsing errors
SqlTableInfoError
Error that arises when attempting to use an invalid SQL identifier
SyntaxParseError
Possible syntax errors
TagError
Errors that can arise in relation to a Tag
Token
Possible lexical tokens

Traits§

BoolTagExprLexicalParse
Implementing types can be lexically parsed to a token stream
BoolTagExprSyntaxParse
Implementing types can be (lexically and) syntactically parsed to a boolean expression tree

Type Aliases§

TagName
An alias of TagComponent
TagValue
An alias of TagComponent
Tags
An alias of BTreeSet<Tag>