hiqlite 0.13.2

Hiqlite - highly-available, embeddable, raft-based SQLite + cache
Documentation
use rust_embed::RustEmbed;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;

pub struct Migrations;

impl Migrations {
    pub fn build<T: RustEmbed>() -> Vec<Migration> {
        let mut files = T::iter()
            .map(|name| {
                let (id, _) = name
                    .split_once('_')
                    .expect("Migration file names must start with `<integer>_<migration_name>");
                let id = id.parse::<u32>().expect(
                    "Migration scripts must start with an increasing integer with \
                    no gaps and starting at index 1",
                );
                (id, name)
            })
            .collect::<Vec<(u32, Cow<'static, str>)>>();

        if files.is_empty() {
            return Vec::default();
        }

        files.sort_by(|(a, _), (b, _)| a.partial_cmp(b).unwrap());
        if let Some((first_id, _)) = files.first()
            && *first_id != 1
        {
            panic!("Migrations must start at index 1");
        }

        let mut res: Vec<Migration> = Vec::with_capacity(files.len());

        for (id, file_name) in files {
            let data = T::get(file_name.as_ref()).unwrap();
            let hash = hex::encode(data.metadata.sha256_hash());
            let content = data.data.to_vec();

            let stripped = file_name
                .strip_suffix(".sql")
                .expect("Migration scripts must always end with .sql");
            let (_, name) = stripped.split_once('_').unwrap();

            let migration = Migration {
                id,
                name: name.to_string(),
                hash,
                content,
            };

            let len = res.len();
            if len > 0 && migration.id != (res[len - 1].id + 1) {
                panic!("");
            }

            res.push(migration);
        }

        res
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Migration {
    pub id: u32,
    pub name: String,
    /// sha256 hash as hex
    pub hash: String,
    pub content: Vec<u8>,
}

/// Applied migrations to the database.
///
/// Can be retrieved with `.query_map("SELECT * FROM _migrations", params!())`
#[derive(Debug, Clone)]
pub struct AppliedMigration {
    pub id: u32,
    pub name: String,
    pub ts: i64,
    /// sha256 hash as hex
    pub hash: String,
}

impl From<&mut crate::Row<'_>> for AppliedMigration {
    fn from(row: &mut crate::Row<'_>) -> Self {
        Self {
            id: row.get::<i64>("id") as u32,
            name: row.get("name"),
            ts: row.get("ts"),
            hash: row.get("hash"),
        }
    }
}