use sqlx::sqlite::SqlitePool; use crate::{app::App, db}; /// Command-line entry point for repairing canonical names in the `hi` database. /// This command may be necessary after an upgrade, if the canonical forms of /// names has changed. It will re-calculate the canonical form of each name in /// the database, based on its display form, and store the results back to the /// database. /// /// This is intended to be used as a Clap [Parser], to capture command-line /// arguments for the `hi-recanonicalize` command: /// /// ```no_run /// # use hi::cli::recanonicalize::Error; /// # /// # #[tokio::main] /// # async fn main() -> Result<(), Error> { /// use clap::Parser; /// use hi::cli::recanonicalize::Args; /// /// let args = Args::parse(); /// args.run().await?; /// # Ok(()) /// # } /// ``` #[derive(clap::Parser)] #[command( version, about = "Recanonicalize names in the `hi` database.", long_about = r#"Recanonicalize names in the `hi` database. The `hi` server must not be running while this command is run. The database at `--database-url` will also be created, or upgraded, automatically."# )] pub struct Args { /// Sqlite URL or path for the `hi` database #[arg(short, long, env, default_value = "sqlite://.hi")] database_url: String, /// Sqlite URL or path for a backup of the `hi` database during upgrades #[arg(short = 'D', long, env, default_value = "sqlite://.hi.backup")] backup_database_url: String, } impl Args { /// Recanonicalizes the `hi` database, using the parsed configuation in /// `self`. /// /// This will perform the following tasks: /// /// * Migrate the `hi` database (at `--database-url`). /// * Recanonicalize names in the `login` and `channel` tables. /// /// # Errors /// /// Will return `Err` if the canonicalization or database upgrade processes /// fail. The specific [`Error`] variant will expose the cause /// of the failure. pub async fn run(self) -> Result<(), Error> { let pool = self.pool().await?; let app = App::from(pool); app.logins().recanonicalize().await?; app.channels().recanonicalize().await?; Ok(()) } async fn pool(&self) -> Result { db::prepare(&self.database_url, &self.backup_database_url).await } } /// Errors that can be raised by [`Args::run`]. #[derive(Debug, thiserror::Error)] #[error(transparent)] pub enum Error { // /// Failure due to `io::Error`. See [`io::Error`]. // Io(#[from] io::Error), /// Failure due to a database initialization error. See [`db::Error`]. Database(#[from] db::Error), /// Failure due to a data manipulation error. See [`sqlx::Error`]. Sqlx(#[from] sqlx::Error), }