rust-query 0.2.1

A query builder using rust concepts.
Documentation
# Type safe queries using the Rust type system
The goal of this library is to allow writing relational database queries using familiar Rust syntax.
The library should guarantee that a query can not fail if it compiles.
This already includes preventing use after free for row ids passed between queries and even database migrations!

Writing queries using this library involves:
- Interact with row/column references as Rust values.
- Lifetimes to check the scopes of row/column references.
- Imperative mutation of row sets with methods like `filter` and `join`.

Notably it does not involve any new syntax or macro, while still being completely type safe.

## Roadmap

This project is under development and there are many things missing.

Query types:
- [x] SELECT
- [x] INSERT
- [ ] DELETE
- [ ] UPDATE

Basic operations:
- [x] Eq, Add, Not, And, Lt, UnwrapOr, IsNotNull, AsFloat
- [ ] Everything else

Advanced operations:
- [x] Aggregate
- [ ] Limit

Backend support:
- [x] sqlite
- [ ] postgres
- [ ] duckdb

Despite these limitations, I am dogfooding this query builder and using it in my own project: [advent-of-wasm](https://github.com/LHolten/advent-of-wasm).

## What it looks like

Define a schema using `enum` syntax:
```rust
use rust_query::migration::schema;

#[schema]
enum MySchema {
    // Enum variants are database tables
    User {
        // This table has one column with String type.
        name: String,
    },
    Image {
        description: String,
        // This column has a foreign key constraint to the User table
        uploaded_by: User,
    },
}
```
Get proof that we are running on a unique thread:
```rust
let mut token = ThreadToken::try_new().unwrap();
```
Initialize a database:
```rust
let database = Prepare::open("my_database.sqlite")
    .create_db_empty()
    .expect("database version is before supported versions")
    // migrations go here
    .finish(&mut token)
    .expect("database version is after supported versions");
```
Perform a transaction!
```rust
let mut transaction = database.write_lock(&mut token);
do_stuff_with_database(&mut transaction);
// After we are done we commit the changes!
transaction.commit();
```
Insert in the database:
```rust
// Lets make a new user 'mike',
let mike = UserDummy { name: "mike" };
let mike_id = db.try_insert(mike).unwrap();
// and also insert a dog picture for 'mike'.
let dog_picture = ImageDummy {
    description: "dog",
    uploaded_by: mike_id,
};
db.try_insert(dog_picture).unwrap();
```
Query from the database:
```rust
// Now we want to get all pictures for 'mike'.
let mike_pictures = db.query(|rows| {
    // Initially there is one empty row.
    // Lets join the pictures table.
    let picture = Image::join(rows);
    // Now lets filter for pictures from mike,
    rows.filter(picture.uploaded_by().eq(mike_id));
    // and finally turn the rows into a vec.
    rows.into_vec(picture.description())
});

println!("{mike_pictures:?}"); // This should print `["dog"]`.
```
The full example code can be found in [insert_and_select.rs](examples/insert_and_select.rs)

Some features not shown in this example are:
- Migrations and unique constraints
- Lookups by unique constraint
- Aggregations


## Examples
For more example queries you can look at [the chinook example](/tests/chinook.rs).

First download the `Chinook_Sqlite.sqlite` from here https://github.com/lerocha/chinook-database/releases and put it in the root of this repository (the working dir).

Then you can run with `cargo test`.