Crates.io | criterium |
lib.rs | criterium |
version | 2.1.1 |
source | src |
created_at | 2024-08-10 21:51:13.733792 |
updated_at | 2024-10-26 15:27:54.3905 |
description | Lightweigt dynamic database queries for rusqlite. |
homepage | |
repository | https://codeberg.org/unobtanium/criterium |
max_upload_size | |
id | 1332740 |
size | 90,550 |
Criterium is a mini-framework to make implementing dynamic conditions and database queries easier.
It does so by supplying the needed datastructures that can be compiled to SQL and a list of values or directly matched against a given value.
Multiple Criteria can also be combined using Boolean logic (chains).
Dynamic also means configurable, Criterium integrates itself with Serde.
It currently integrates with rusqlite.
Also see the concepts document.
Usually when building database query one starts out with a relatively simple function:
fn fetch_bookmark_information(
is_favorite: Option<bool>,
name_contains: Option<String>,
tags: Vec<String>,
but_not_tags: Vec<String>,
// …
) -> Vec<Bookmark>;
But as you can see even from this simple example: This won't scale very well and writing the SQL generation for that by hand is a tedious nightmare (even when switching over to structs).
And most of the time one doesn't even need all the options available. This is where Criterium comes in.
With Criterium you have an enum that describes all the possible queries for a datatype.
enum BookmarkCriterium {
IsFavorite(BooleanCriterium),
Name(StringCriterium),
HasTag(StringCriterium),
// …
}
impl ToRusqliteQuery for BookmarkCriterium {
fn get_sql_where(&self) -> String {
match self {
Self::IsFavorite(c) => c.get_sql_where("favorite".to_string()),
Self::Name(c) => c.get_sql_where("name".to_string()),
}
}
fn get_where_values(&self) -> Vec<Value> {
match self {
Self::IsFavorite(c) => c.get_where_values(),
Self::Name(c) => c.get_where_values(),
}
}
}
Note: Not all of the datatypes of the example are implemented yet, but it should be pretty straigtforward to implement them outside of Criterium.
Multiple of these are then combined using a CriteriumChain
:
let chain_builder: CriteriumChainBuilder<BookmarkCriterium> =
CriteriumChainBuilder::and(true);
chain_builder.add_criterium(
BookmarkCriterium::IsFavorite(true.into()));
chain_builder.add_criterium(
BookmarkCriterium::HasTag("tag".into()));
chain_builder.add_criterium(
BookmarkCriterium::HasTag(StringCriterium::HasPrefix("useful:")));
chain_builder.add_criterium(
BookmarkCriterium::Name(StringCriterium::Contains("foo")));
let bookmarks = database.query_bookmarks(chain_builder.to_chain());
While at first glance this looks more verbose (it is) this can be assembled on the fly and is more flexible (Noticed how I snug a prefix match on a tag in there?).
The SQL in the background simply has a WHERE
with the output of the chain.get_sql_where()
concatenated after and the values taken from chain.get_where_values()
, you only have to provide the values your Criterium relies on in the SQL query.
As you can tell this is useful when there are a lot of things to query for, and also pretty opinioated, it may oe may not fit your project.
Isn't that an Object Relational Model (ORM) like in django?
Yes, kind of. But very lightweight and barebones … and the compiler can verify most of it for you.
By default all integrations that require external crates are disabled. More integrations to come soonish™.
rusqlite
- Enables rusqlite integration (compiling criteria to SQL)chrono
- Enables chrono integration (passing a DateTime
in place of a number)serde
- Enables serializing and deserializing criteium-chains and default criteria using serde.full
- Enable all features, useful for building documentation, for dependencies you should explicitly enable all features you need.Note: If you are reding this from the rust domunetation, the links are broken.