Day 3: Migratory dirs

Just a quick one today because I am sleepy!

I need to be able to store the URLs for feeds to sync from. I considered using a config file for this (such as KDL) but I think a CLI-managed database would be nice instead. Maybe also with the option to export/import from a config file so you can move between machines. Hm… I may revisit this at some point. I know people like to be able to sync their dotfiles and have that Just Work. Maybe URLs should be in a file and everything else a DB?

Regardless, we need some sort of database to keep track of when we last synced etc. To keep things simple, I’ll use SQLite, which seems like the obvious choice. Embedding a database into a program is exactly what it was made for. So, how do we manage that?

Database migrations

If you wish to make a CLI from scratch, you must first create the database. I started writing my own database migration system using rusqlite directly before I realized that it would be a real pain, and probably a solved problem. I instead searched for an existing solution and found refinery. It seems fully featured, though I don’t love the hard requirement to use procedural macros, which can slow compile times. I’m forcing myself to not go all-in on dependency reduction early on though. Besides, reqwest et al. are much worse for that.

Like last time, I’m using directories to figure out where the data should live.

fn main() -> Result<()> {
    // ---8<--- snip
    let data_dir = project_dirs.data_dir();
    std::fs::create_dir_all(&data_dir)?;
    let db_path = data_dir.join("seance.db");
    setup_db(&db_path)?;
    // ---8<--- snip
}

mod embedded {
    use refinery::embed_migrations;
    embed_migrations!("./migrations");
}

fn setup_db(path: &Path) -> Result<()> {
    let mut conn = Connection::open(path)?;
    embedded::migrations::runner().run(&mut conn)?;

    Ok(())
}

This works! Checking using the sqlite3 CLI, I see that it has created an (empty) table called refinery_schema_history. Séance will now use this table to keep track of what migrations need to be run. The migration files themselves will be embedded in the binary during compiliation.