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),
}
|