Crate rql

Source
Expand description

§Description

RQL (Rusty Query Language) is a library design to bring sql-like logic to Rust. However, bear in mind that there is no traditional database here, just some traits and adapters applied to iterators and hashmaps.

§Important Note

rql does not provide a real database. All data is stored in memory. This pseudo-database can be saved to and loaded from the disk via serialization and deserialization from serde. A number of serialization protocols are supported so that you may choose one to suit your speed, size, and backward-compatibility needs.

§Schema

To use an rql database, you must first define some schema. Every table is defined by a struct representing a single entry. In this example, we will define three tables: User, Group, and Member.

use rql::prelude::*;

#[derive(Serialize, Deserialize)]
struct User {
    name: String,
    age: u8,
}

#[derive(Serialize, Deserialize)]
struct Group {
    name: String,
}

#[derive(Serialize, Deserialize)]
struct Member {
    user_id: Id<User>,
    group_id: Id<Group>,
    permission: bool,
}

Unique id fields are not necessary, as every entry is automatically given a unique identifier. References to entries in other tables are denoted with Id<T>.

To make the actual schema, use the schema! macro:

schema! {
    MySchema {
        user: User,
        group: Group,
        member: Member,
    }
}

§Database operations

Below are a few simple ways of interfacing with the database.



// Create a new database with the previously defined schema
// We pass a folder name for the database files as well as a representation type
let db = MySchema::new("test_database_example", HumanReadable).unwrap();

// Insert values into the database
// Insertion returns the new row's id
let dan   = db.user_mut().insert(User { name: "Dan".into(),   age: 25 });
let steve = db.user_mut().insert(User { name: "Steve".into(), age: 39 });
let mary  = db.user_mut().insert(User { name: "Mary".into(),  age: 31 });

let admin  = db.group_mut().insert(Group { name: "Admin".into()       });
let normal = db.group_mut().insert(Group { name: "Normal User".into() });

db.member_mut().insert(Member { user_id: dan,   group_id: admin,  permission: true  });
db.member_mut().insert(Member { user_id: steve, group_id: normal, permission: true  });
db.member_mut().insert(Member { user_id: mary,  group_id: normal, permission: false });

// Data can easily be looked up by id
db.user_mut().get_mut(dan).unwrap().age += 1;
let dan_age = db.user().get(dan).unwrap().age;
assert_eq!(dan_age, 26);

// Data can be selected from a table
let ages: Vec<u8> = db.user().select(|user| user.age).collect();

// Use `wher` to filter entries
let can_run_for_president: Vec<String> =
    db.user()
        .wher(|user| user.age >= 35)
        .select(|user| user.name.clone())
        .collect();

// Table intersections are done using `relate`
// A function relating the tables is required
for (user, permission) in db.user()
    .relate(
        &*db.member(),
        |user, member| user.id == member.user_id && member.group_id == normal
    )
    .select(|(user, member)| (&user.data.name, member.permission)) {
    println!("{} is a normal user with permission = {}", user, permission);
}

// Rows can be updated with `update`
for mut user in db.user_mut().update() {
    user.age += 1;
}

// Rows can be deleted in a few ways

// By id
db.user_mut().delete_one(steve);

// With a where clause
db.member_mut().delete_where(|member| member.permission);

// With an iterator over ids
db.user_mut().delete_iter(|_| vec![dan, mary]);

// Changes to the database are automatically saved, so they can be loaded again
let db_copy = MySchema::new("test_database_example", HumanReadable).unwrap();
assert_eq!(db.user().len(),   db_copy.user().len()  );
assert_eq!(db.group().len(),  db_copy.group().len() );
assert_eq!(db.member().len(), db_copy.member().len());

Re-exports§

pub use crate::repr::*;

Modules§

example_schema
Example schema generated by the schema! macro
prelude
A prelude for commonly used imports
repr
Ways of serializing/deserializing data

Macros§

schema
Macro for generating a schema struct

Structs§

Id
An id for indexing rows
OwnedRow
An row of owned data cloned from a Table
Relate
A relationship between two tables
Row
A row in a Table
RowIter
An iterator over rows in a Table
RowIterMut
An mutable iterator over rows in a Table
RowMut
A mutable row in a Table
Select
An adaptor for selecting data
Table
A table abstraction akin to a table in a real schema
TableGuard
An immutable guard to a Table
TableGuardMut
A mutable guard to a Table
Where
A clause for searching through rows and limiting the returned values, similar to a sql WHERE

Enums§

Error
An error type for rql

Traits§

HasRows
A trait for accessing rows
HasRowsMut
A trait for mutably accessing rows
Idd
A trait for getting row Ids

Type Aliases§

Result
A result type for rql