liter 0.0.7

Experimental library for using SQLite with minimal SQL
Documentation

Experimental library for using SQLite with minimal SQL

Liter generates complete SQL schema definitions from ordinary Rust struct definitions at compile-time. Built on top of [rusqlite], it leverages powerful user-implementable traits to generate well-constrained and type-aware table definitions.

SQL is not generated by the derive macros directly: they can only operate textually, so they invoke const functions instead. These then have access to the types and their trait implementations, which give you control over how the SQL is generated.

Basic Example

Here's a very simple example of a database with just one table.

use liter::{database, Table};

#[database]
struct ShoppingList (
    Item
);

#[derive(Table, PartialEq, Debug)]
struct Item {
    count: u32,
    name: String
}

let list = ShoppingList::create_in_memory()?;

let oranges = Item {
    count: 3,
    name: "Orange".to_string(),
};

list.insert(&oranges)?;
let items = list.get_all::<Item>()?;

assert_eq!(oranges, items[0]);
# Ok::<(), rusqlite::Error>(())

Liter generates the following SQL schema for you, as well as the SELECT & INSERT statements.

BEGIN TRANSACTION;
CREATE TABLE item (
    count INTEGER NOT NULL,
    name TEXT NOT NULL
) STRICT;
END TRANSACTION;

Schema Generation Overview

There are several traits that make up a liter database, but it is actually quite straight-forward. We'll start at the top (or root, if you'll think of it as a tree), and work our way down.

A [Schema] defines a liter database by its constituent [Table]s, which is to say it defines the database by the tables it contains. This trait is implemented by the #[database] proc-macro on a tuple struct declaring the [Table] types that are part of the database.

The [Table] trait is implemented by using #[derive(Table)] on a regular struct. Each row of the generated SQL table (named after the struct) will store an instance of this struct. You might assume that each field of the struct represents a column in its table, and that is almost correct.

The [Value] trait is an intermediary layer which represents one or more [Column]s. As such, it is implemented for all types that implement [Column] -- which, as you may have guessed, represents a primitive data type that can be stored in a single SQL column -- but it is also implemented (and can be implemented by you) for types that require multiple SQL columns. In fact, a [Value] can be defined not only as a set of [Column]s, but as a set of [Value]s.

Though it is admittedly rather generically named, the [Value] trait is an important abstraction that allows defining database tables with reusable & composable components rather than just column primitives. For instance, it enables easy foreign key references through the generic [Ref] struct, even to tables with composite primary keys.

Example with Primary & Foreign Keys

This slightly more complicated example showcases foreign key references & composite primary keys.

use liter::{database, Table, Id, Ref};

#[database]
struct Dictionary (
    Language,
    Word
);

#[derive(Table)]
struct Language {
    #[key]
    id: Id,
    name: String
}

#[derive(Table)]
struct Word {
    #[key]
    language: Ref<Language>,
    #[key]
    word: String,
    definition: String
}
let dict = Dictionary::create_in_memory()?;

let mut lang = Language {
    id: Id::NULL,
    name: "Latin".to_string()
};
dict.create(&mut lang)?; // assigns the newly created Id

let word = Word {
    language: Ref::make_ref(&lang),
    word: "nunc".to_string(),
    definition:
        "now, at present, at this time, at this very moment".to_string()
};
dict.insert(&word)?;
# Ok::<(), rusqlite::Error>(())

Note that this time, a few more SQL statements were generated as part of the [HasKey] impls for Language & Word.

And here's the generated schema:

BEGIN TRANSACTION;
CREATE TABLE language (
    id INTEGER NOT NULL,
    name TEXT NOT NULL,
    PRIMARY KEY ( id )
) STRICT;
CREATE TABLE word (
    language INTEGER NOT NULL,
    word TEXT NOT NULL,
    definition TEXT NOT NULL,
    PRIMARY KEY ( language, word ),
    FOREIGN KEY (language) REFERENCES language
        ON UPDATE RESTRICT
        ON DELETE RESTRICT
        DEFERRABLE INITIALLY DEFERRED
) STRICT;
END TRANSACTION;