<!--
SPDX-FileCopyrightText: 2025 Slatian

SPDX-License-Identifier: LGPL-3.0-only
-->

# Criterium

[Documentation](https://docs.rs/criterium) | [Codeberg](https://codeberg.org/unobtanium/criterium)

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](./Serde.md).

It currently integrates with [rusqlite](https://docs.rs/rusqlite/latest/rusqlite/).

## How does it work?

Also see the [concepts document](./Concepts.md).

Usually when building database query one starts out with a relatively simple function:

```rust
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.

```rust
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(),
		}
	}
}
```

<b>Note:</b> 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`:

```rust
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.

## Feature Flags

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.

## What is in here?

<b>Note:</b> If you are reding this from the rust domunetation, the links are broken.

* [boolean](src/boolean/) - A Criterium for matching boolean values.
	* [rusqlite.rs](src/boolean/rusqlite.rs) - Rusqlite integration.
* [number](src/number/) - A Criterium for matching numbers.
	* [rusqlite.rs](src/number/rusqlite.rs) - Rusqlite integration.
* [string](src/string/) - A Criterium for matching text.
	* [rusqlite.rs](src/string/rusqlite.rs) - Rusqlite integration.
* [search](src/search) - A Criterium for integrating full text search capabilities.
* [chain](src/chain/) - A Criterium for connecting other criteria with boolean operators
	* [builder.rs](src/chain/builder.rs) - A builder struct to make it easier to construct chains.
	* [rusqlite.rs](src/chain/rusqlite.rs) - Rusqlite integration.
* [rusqlite.rs](src/rusqlite.rs) - Traits for integrating with rusqlite.

## License

Criterium is licensed under the [LGPL-3.0-only license](./LICENSES/LGPL-3.0-only.txt).

The project aims to be compliant with version 3.3 of the [reuse](https://reuse.software) specification.
