runledger-postgres 0.1.1

PostgreSQL persistence layer for the Runledger durable job and workflow system
Documentation
use std::collections::BTreeMap;
use std::fs;
use std::path::{Path, PathBuf};

fn main() {
    let manifest_dir =
        PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR must be set"));
    let vendored_dir = manifest_dir.join("migrations");
    let canonical_dir = manifest_dir.join("../migrations");

    println!("cargo:rerun-if-changed={}", vendored_dir.display());
    if canonical_dir.exists() {
        println!("cargo:rerun-if-changed={}", canonical_dir.display());
        enforce_migration_sync(&canonical_dir, &vendored_dir);
    }
}

fn enforce_migration_sync(canonical_dir: &Path, vendored_dir: &Path) {
    let canonical = load_sql_files(canonical_dir);
    let vendored = load_sql_files(vendored_dir);

    if canonical != vendored {
        panic!(
            "runledger-postgres/migrations is out of sync with the canonical root migrations/ directory; run ./scripts/refresh-sqlx-cache.sh"
        );
    }
}

fn load_sql_files(dir: &Path) -> BTreeMap<String, String> {
    let mut files = BTreeMap::new();

    let entries = fs::read_dir(dir)
        .unwrap_or_else(|err| panic!("read migration directory {}: {err}", dir.display()));
    for entry in entries {
        let entry =
            entry.unwrap_or_else(|err| panic!("read migration entry in {}: {err}", dir.display()));
        let path = entry.path();
        if path.extension().and_then(|ext| ext.to_str()) != Some("sql") {
            continue;
        }

        let file_name = path
            .file_name()
            .and_then(|name| name.to_str())
            .unwrap_or_else(|| panic!("migration file name is not valid UTF-8: {}", path.display()))
            .to_owned();
        let contents = fs::read_to_string(&path)
            .unwrap_or_else(|err| panic!("read migration file {}: {err}", path.display()));
        files.insert(file_name, contents);
    }

    files
}