pg-worm 0.6.2

An async, fully-typed and straightforward ORM for PostgreSQL databases
Documentation
# `pg-worm`
[![Latest Version](https://img.shields.io/crates/v/pg-worm.svg)](https://crates.io/crates/pg-worm)
![GitHub Actions Testing](https://github.com/Einliterflasche/pg-worm/actions/workflows/rust.yml/badge.svg) 
[![docs](https://docs.rs/pg-worm/badge.svg)](https://docs.rs/pg-worm)
[![license](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

### *P*ost*g*reSQL's *W*orst *ORM*
`pg-worm` is a straightforward, fully typed, async ORM and Query Builder for PostgreSQL.
Well, at least that's the goal. 

## Features/Why `pg-worm`?

- Existing ORMs are not **`async`**, require you to write migrations or use a cli. 
`pg-worm`'s explicit goal is to be **easy** and to require **no setup** beyond defining your types. 

- `pg-worm` also features **built-in pooling** and a **concise syntax**.

- `pg-worm` **doesn't get in your way** - easily include raw queries while still profiting off the other features.

## Usage
This library is based on [`tokio_postgres`](https://docs.rs/tokio-postgres/0.7.8/tokio_postgres/index.html) and is intended to be used with [`tokio`](https://tokio.rs/).

Fortunately, using `pg-worm` is very easy.

Simply derive the `Model` trait for your type, connect to your database 
and you are ready to go!

Here's a quick example: 

```rust
// Import the prelude to get started quickly
use pg_worm::prelude::*;

#[derive(Model)]
struct Book {
    // An auto-generated primary key
    #[column(primary_key, auto)]
    id: i64,
    title: String
    author_id: i64
}

#[derive(Model)]
struct Author {
    #[column(primary_key, auto)]
    id: i64,
    name: String
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // First create a connection. This can be only done once.
    Connection::build("postgres://postgres:postgres@localhost:5432").connect()?;

    // Then, create tables for your models. 
    // Use `try_create_table!` if you want to fail if a
    // table with the same name already exists.
    //
    // `force_create_table` drops the old table,
    // which is useful for development.
    //
    // If your tables already exist, skip this part.
    force_create_table!(Author, Book).await?;

    // Next, insert some data.
    // This works by passing values for all
    // fields which aren't autogenerated.
    Author::insert("Stephen King").await?;
    Author::insert("Martin Luther King").await?;
    Author::insert("Karl Marx").await?;
    Book::insert("Foo - Part I", 1).await?;
    Book::insert("Foo - Part II", 2).await?;
    Book::insert("Foo - Part III", 3).await?;

    // Let's start with a simple query for all books:
    let books = Book::select().await?; // Vec<Book>
    assert_eq!(books.len(), 3);

    // You can also search for a specific book.
    // Adding a `WHERE` clause is as simple as
    // calling a method on the respective field:
    let book = Book::select_one()
        .where_(Book::title.eq(&"Foo - Part I".to_string()))
        .await?; // Option<Book>
    assert!(book.is_some());

    // Or update exsisting records:
    let books_updated = Book::update()
        .set(Book::title, &"Foo - Part III".to_string())
        .where_(Book::title.eq(&"Foo - Part II".to_string()))
        .await?; // u64
    assert_eq!(books_updated, 1);

    // Or delete a book, you don't like:
    let books_deleted = Book::delete()
        .where_(Book::title.eq(&"Foo - Part III".to_string()))
        .await?; // u64
    assert_eq!(books_deleted, 2);

    Ok(())
}
```

If you want to see more code examples, have a look at the [tests directory](https://github.com/Einliterflasche/pg-worm/tree/main/pg-worm/tests).

## Query Builders
As you can see above, `pg-worm` allows you to build queries by chaining methods on so called 'builders'. 
For each query type `pg-worm` provides a respective builder (except for `INSERT` which is handled differently).

These builders expose a set of methods for building queries. Here's a list of them:

Method | Description | Availability
-------|-------------|-------------
`where_` | Attach a `WHERE` clause to the query. | All builders (`Select`, `Update`, `Delete`)
`where_raw` | Same as `where_` but you can pass raw SQL. | All builders (`Select`, `Update`, `Delete`) 
`set` | `SET` a column's value. Note: this method has to be called at least once before you can execute the query. | `Update`
`limit`, `offset` | Attach a [`LIMIT` or `OFFSET`]https://www.postgresql.org/docs/current/queries-limit.html to the query. | `Select`

## Filtering using `WHERE`
`.where_()` can be used to easily include `WHERE` clauses in your queries. 

This is done by passing a `Where` object which can be constructed by calling methods on the respective column. 
`pg-worm` automatically constructs a constant for each field 
of your `Model`.

A practical example would look like this:

```rust
let where_: Where<'_> = MyModel::my_field.eq(&5);
```

### Available methods

Currently, the following methods are implemented:

Function | Description | Availability
---------|-------------|-------------
`eq` | Checks for equality. | Any type
`gt`, `gte`, `lt`, `lte` | Check whether this column's value is greater than, etc than some other value. | Any type which implements [`PartialOrd`]https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html. Note: it's not guaranteed that Postgres supports these operator for a type just because it's `PartialOrd`. Be sure to check the Postgres documentation for your type beforehand.
`null`, `not_null` | Checks whether a column is `NULL`. | Any `Option<T>`. All other types are not `NULL`able and thus guaranteed not to be `NULL`.
`contains`, `contains_not`, `contains_all`, `conatains_none`, `contains_any` | Array operations. Check whether this column's array contains a value, a value _not_, or any/all/none values of another array. | Any `Vec<T>`.

### Boolean logic

You can also chain/modify these filters with standard boolean logic:

```rust
Book::select()
    .where_(!Book::id.eq(&1) & Book::id.gt(&3))
    .await?;
```

Operator/Method | Description
----------------|------------
`!`, `.not()` | Negate a filter using a locigal `NOT`
`&`, `.and()` | Combine two filters using a logical `AND`
`\|\|`, `.or()` | Combine two filters using a logical `OR`


### Executing a query

After having finished building your query, you can simply call `.await`. 
This will turn the builder into a `Query` object which is then executed asynchronously.

Executing a query will always result in a `Result`.

## Raw queries

Though these features are nice, they are not sufficient for all applications. This is why you can easily execute custom queries and still take advantage of automatic parsing, etc:

```rust
// NOTE: You have to pass the exact type that PostgreSQL is 
// expecting. Doing otherwise will result in a runtime error.
let king_books = Book::query(r#"
        SELECT * FROM book 
        JOIN author ON author.id = book.author_id
        WHERE POSITION(? in author.name) > 0 
    "#, 
    vec![&"King".to_string()]
).await?;
assert_eq!(king_books.len(), 2);
```

Alse see `.where_raw` on query builders by which you can pass a raw condition without needing to write the whole query yourself.

## Transactions

`pg-worm` also supports transactions. You can easily execute any query inside a `Transaction` and only commit when you are satisfied. 

`Transaction`s are automatically rolled-back when dropped, unless they have been committed beforehand.

Here's an example:

```rust
use pg_worm::prelude::*;

#[derive(Model)]
struct Foo {
    bar: i64
}

async fn foo() -> Result<(), Box<dyn std::error::Error>> {
    // Easily create a new transaction
    let transaction = Transaction::begin().await?;

    // Execute any query inside the transaction
    let all_foo = transaction.execute(
        Foo::select()
    ).await?;   

    // Commit the transaction when done.
    // If not committed, transaction are rolled back
    // when dropped.
    transaction.commit().await?;
}
```

## Supported types
The following is a list of all supported (Rust) types and which PostgreSQL type they are mapped to.
Rust type | PostgreSQL type
----------|---------------------
`bool` | `bool`
`i32` | `int4`
`i64` | `int8`
`f32` | `float4`
`f64` | `float8`
`String` | `TEXT`
`Option<T>`* | `T` (but the column becomes `nullable`)
`Vec<T>`* | `T[]`

_*`T` must be another supported type. Nesting and mixing `Option`/`Vec` is currently not supported._

## MSRV
The minimum supported rust version is `1.70` as this crate uses the recently introduced `OnceLock` from the standard library.

## License
This project is dual-licensed under the MIT and Apache 2.0 licenses.