Skip to main content

Module store

Module store 

Source
Expand description

Persistent data for a bot, in one SQLite file.

This module needs the store feature.

A Store is one open SQLite database. A bot gives each part of itself (for example, each group of handlers) its own Namespace. A namespace keeps data in two ways:

SQLite calls block the thread. All these functions run the call on Tokio’s blocking pool, so a slow query does not stop the runtime.

§Key/value example

use ircbot::store::Store;

let store = Store::memory()?;
let karma = store.namespace("karma")?;

karma.set("alice", &3_i64).await?;
let score: Option<i64> = karma.get("alice").await?;
assert_eq!(score, Some(3));

A value of a #[derive(Serialize, Deserialize)] struct works the same way. If the struct gets a new field later, give the field #[serde(default)], so a value stored before the change still decodes.

§SQL example

use ircbot::store::Store;

// A real bot gives a file path to `Store::open`.
let store = Store::memory()?;
let quotes = store.namespace("quotes")?;

// Add new steps at the end of the list. Do not change or remove a step
// that a database already has.
quotes
    .migrate(&["CREATE TABLE quotes_quote (id INTEGER PRIMARY KEY, text TEXT NOT NULL)"])
    .await?;

quotes
    .sql(|conn| conn.execute("INSERT INTO quotes_quote (text) VALUES (?1)", ["hello"]))
    .await?;
let count: i64 = quotes
    .sql(|conn| conn.query_row("SELECT count(*) FROM quotes_quote", [], |row| row.get(0)))
    .await?;
assert_eq!(count, 1);

To use a store in handlers, put the namespace in the bot state. An open database has no useful Default, so use no_default and start the bot with new_with_state. The sqlite_bot example of this crate is a complete bot that does this:

ⓘ
struct Data { quotes: Namespace }

#[bot(state = Data, no_default)]
impl QuoteBot { /* … */ }

let store = Store::open("bot.db")?;
let quotes = store.namespace("quotes")?;
quotes.migrate(QUOTE_MIGRATIONS).await?;
QuoteBot::new_with_state("quotebot", "irc.example.net:6667", ["#rust"], Data { quotes })
    .main_loop()
    .await

§Names

A namespace name starts with a lowercase ASCII letter. The other characters are lowercase ASCII letters, digits, and _. Thus a name is safe to use in SQL as a prefix of a table name.

SQLite keeps table names that start with sqlite_ for its own use. Thus a namespace cannot have the name sqlite, or a name that starts with sqlite_.

All namespaces share one database. Give each table the name of its namespace as a prefix (quotes_quote), so two namespaces do not use the same table. The store cannot make sure of this, because sql gives the full connection.

The store keeps its own data in tables whose names start with _ircbot. Do not change these tables.

Re-exports§

pub use rusqlite;

Structs§

Namespace
The part of a Store that one part of the bot uses.
Store
One open SQLite database.

Enums§

StoreError
An error from the store.