rust-rel8 0.1.4

Rel8 but in rust
Documentation

This is a WIP library attempting to emulate the Haskell Rel8 library.

In essence, it allows you to write queries like so:

#[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 q = query::<(Expr<String>, Expr<String>)>(|q| {
    // select all rows from the users and posts tables.
    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.