main.rs
Overview
This file implements a command-line tool for managing and migrating the schema of an SQLite database named bm-archive.db associated with the Acki-Nacki DB project. It facilitates database schema version control by applying migrations stored as embedded resources, ensuring the database is created, initialized, and upgraded or downgraded to a specified version. The migration logic relies on the rusqlite and rusqlite_migration crates, employing migrations embedded at compile time via the include_dir crate.
Key Components
Enum MTarget<'a>
enum MTarget<'a> {
BlockManager(Migrations<'a>),
}
Defines migration targets, currently supporting only the
BlockManagervariant which wraps migration sets of typeMigrations.Facilitates extensibility for managing different database components or modules with separate migrations.
Struct Args
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
#[arg(short = 'p')]
db_path: String,
#[arg(long = "block-manager", value_name = "SCHEMA_VERSION", num_args = 0..=1, default_missing_value = "latest")]
bm: Option<String>,
}
Represents the command-line arguments parsed via the
clapcrate.
Fields
Field | Description | Usage Example |
|---|---|---|
| Path to the directory containing the database file |
|
| Optional target schema version for the block-manager database migration. Defaults to |
|
Function parse_version
fn parse_version(v: Option<String>, target: MTarget) -> anyhow::Result<u32>
Parses the target migration version from a user-provided string or defaults to the latest migration version.
Validates that the requested version exists and is not greater than the latest available migration.
Parameters
Parameter | Description |
|---|---|
| Optional string representing desired version or |
| Migration target specifying which migrations to query. |
Returns
Ok(u32): Parsed version number.Err: If version string is invalid or version number is unavailable.
Usage Example
let version = parse_version(Some("3".to_string()), MTarget::BlockManager(migrations))?;
Function get_latest_migration_version
fn get_latest_migration_version(target: MTarget) -> anyhow::Result<u32>
Retrieves the highest available migration version from the embedded migration directories.
Validates the migration set before scanning directories.
Parses directory names expecting a numeric prefix followed by a dash (e.g.,
001-init).
Parameters
Parameter | Description |
|---|---|
| Migration target for which to get the latest version. |
Returns
Ok(u32): Latest migration version number.Err: If migration validation fails or parsing issues occur.
Function main
fn main() -> anyhow::Result<()>
Entry point of the migration tool.
Parses command-line arguments.
Ensures the target database directory exists.
Opens or creates the
bm-archive.dbSQLite database.Retrieves and prints the current database schema version and the latest migration version.
If a migration version is specified via CLI, performs an upgrade or downgrade to that version.
Workflow
Parse CLI arguments using
Args::parse().Create database directory if missing.
Open SQLite connection to
bm-archive.db.Query current schema version from SQLite PRAGMA
user_version.Load migrations embedded in
M_BLOCK_MANAGER.Get latest migration version.
Display current and latest schema versions.
If migration target specified:
Parse target version.
Compare with current version.
Perform migration up or down accordingly.
Exit gracefully.
Error Handling
Directory creation failure prints to stderr and exits with error code 1.
Migration or database errors are propagated via
anyhow::Result.
Module tests
Contains a single test verifying that the embedded migrations for BlockManager are valid.
Uses the
lazy_staticcrate to instantiate migrations once.Calls
validate()method on migrations ensuring migration files are correctly structured.
Implementation Details
Embedded Migrations: The migration SQL scripts are embedded at compile time using
include_dir!macro pointing to themigrations/bm-archivedirectory. This allows bundling migration files into the binary, ensuring migration resources are always available.Migration Validation: Before using migrations,
validate()is called to confirm migration sequence correctness.Version Parsing: The file/folder naming convention for migrations drives version detection by extracting numeric prefixes.
Database Versioning: Uses SQLite
PRAGMA user_versionto track the schema version applied.Migration Tooling: Uses
rusqlite_migration::MigrationsAPI to apply migrations in order.
Interaction with Other System Components
Database: Directly interacts with the
bm-archive.dbSQLite database file, performing schema version queries and migrations.Migration Files: Reads migrations embedded from the
migrations/bm-archivedirectory, which is structured as part of the project resources.Command Line Interface: Provides a CLI interface for users or scripts to manage database schema versions.
External Crates:
clapfor argument parsing.include_dirfor embedding migration files.rusqlitefor SQLite connections and operations.rusqlite_migrationfor migration orchestration.indocfor multi-line formatted strings output.
Visual Diagram
flowchart TD
A["main()"] --> B["Parse CLI Args (Args)"]
B --> C{Create DB Directory?}
C -- Yes --> D[Open SQLite Connection]
D --> E["Get current schema version (PRAGMA user_version)"]
E --> F["Load Embedded Migrations (M_BLOCK_MANAGER)"]
F --> G[Get Latest Migration Version]
G --> H[Print DB Info]
H --> I{Migration Version Specified?}
I -- No --> J[Exit]
I -- Yes --> K[Parse Target Version]
K --> L{Current Version == Target?}
L -- Yes --> M[Print "Up to date"]
L -- No --> N{Current < Target?}
N -- Yes --> O[Upgrade DB Schema]
N -- No --> P[Downgrade DB Schema]
O & P --> Q[Apply migration via rusqlite_migration]
Q --> J[Exit]
This flowchart summarizes the main workflow inside the main function, highlighting argument parsing, directory and database setup, migration version determination, and migration execution.
Usage Example
To create or migrate a database at path ./data/db to the latest schema version:
cargo run -- -p ./data/db
To migrate the block-manager database to version 3:
cargo run -- -p ./data/db --block-manager 3
Notes on Migration Versioning
Migration directories are expected to be named with a numeric prefix and a dash, e.g.,
001-init, 002-add-table.The latest migration version is determined by the highest numeric prefix in the directory names.
The tool enforces that requested versions are not beyond the latest version available.
Migrations can be upgraded or downgraded, enabling flexible version control of the database schema.
Dependencies and Imports Overview
std::fs and
std::path::PathBuffor filesystem and path management.clap::Parser for CLI argument parsing.
include_dirfor embedding migration directories.indoc::formatdocfor formatted multi-line string output.rusqlitefor SQLite DB connection and operations.rusqlite_migration::Migrationsfor managing and applying migrations.
Error Handling
All functions return
anyhow::Resultallowing use of?operator for error propagation.Directory creation errors are caught explicitly and reported before exiting.
Migration validation errors and version parsing errors cause the tool to exit with descriptive messages.
SQLite errors propagate as anyhow::Error.
Testing
The test suite:
Uses
lazy_staticto initialize migrations once.Validates that the embedded migrations for block manager are consistent and correct using the
.validate()method.Ensures migration files are present and properly structured before applying in production.