summaryrefslogtreecommitdiff
path: root/src/cli/recanonicalize.rs
blob: 9db5b77b28cd2e9423b8de6a69b13c276709f2c8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
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<SqlitePool, db::Error> {
        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),
}