<h1 align="center">Rust Rel8</h1>
<h2 align="center">Rust reimplementation of Rel8</h2>
<p align="center">
<a href="https://crates.io/crates/rust-rel8"><img alt="Crates.io Version" src="https://img.shields.io/crates/v/rust-rel8"></a>
<a href="https://docs.rs/rust-rel8/"><img alt="docs.rs" src="https://img.shields.io/docsrs/rust-rel8"></a>
</p>
This is a WIP library attempting to emulate the Haskell
[Rel8](https://github.com/circuithub/rel8) library.
In essence, it allows you to write queries like so:
``` rust
#[derive(Debug, PartialEq, rust_rel8_derive::TableStruct)]
struct User<'scope, Mode: TableMode> {
id: Mode::T<'scope, i32>,
name: Mode::T<'scope, String>,
age: Mode::T<'scope, i32>,
}
impl<'scope> User<'scope, NameMode> {
const SCHEMA: TableSchema<Self> = TableSchema {
name: "users",
columns: User {
id: "id",
name: "name",
age: "age",
},
};
}
#[derive(Debug, PartialEq, rust_rel8_derive::TableStruct)]
struct Post<'scope, Mode: TableMode> {
id: Mode::T<'scope, i32>,
user_id: Mode::T<'scope, i32>,
contents: Mode::T<'scope, String>,
}
impl<'scope> Post<'scope, NameMode> {
const SCHEMA: TableSchema<Self> = TableSchema {
name: "posts",
columns: Post {
id: "id",
user_id: "user_id",
contents: "contents",
},
};
}
// We're injecting the values manually here, but we could as well use Query::each
// to select from a real table.
let demo_users = vec![
User::<ValueMode> {id: 0, name: "Undine".to_owned(), age: 3,},
User {id: 1, name: "Leschy".to_owned(), age: 3,},
User {id: 2, name: "Huldra".to_owned(), age: 2,},
];
let demo_posts = vec![
Post::<ValueMode> {id: 0, user_id: 0, contents: "Croak".to_owned(),},
Post {id: 1, user_id: 1, contents: "Quak!".to_owned(),},
Post {id: 2, user_id: 1, contents: "Hello".to_owned(),},
];
let user = q.q(Query::values(demo_users.shorten_lifetime()));
let post = q.q(Query::values(demo_posts.shorten_lifetime()));
// introduce a join condition using a filter
// this will cause postgres to turn this from a cross join into an inner join.
q.where_(user.id.clone().equals(post.user_id.clone()));
// select the user name and post contents out, and return a tuple
(user.name, post.contents)
})
.order_by(|x| (x.clone(), sea_query::Order::Asc));
let rows = q.all(&mut *pool).await.unwrap();
assert_eq!(
vec![
("Leschy".to_owned(), "Hello".to_owned()),
("Leschy".to_owned(), "Quak!".to_owned()),
("Undine".to_owned(), "Croak".to_owned())
],
rows
)
```
In this query builder, joins are also left to the database to figure out. The
user thinks of each `let ... = q.q(...);` inside `query` as being cartesian
produced. The database narrows these cartesian products down to inner or left
joins depending on the `where` clauses.
I'll add some more examples in the future, but you can also look at the tests in
[lib.rs](./tests/queries.rs).