about summary refs log tree commit diff stats
path: root/yt/src/storage/migrate/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'yt/src/storage/migrate/mod.rs')
-rw-r--r--yt/src/storage/migrate/mod.rs320
1 files changed, 0 insertions, 320 deletions
diff --git a/yt/src/storage/migrate/mod.rs b/yt/src/storage/migrate/mod.rs
deleted file mode 100644
index badeb6f..0000000
--- a/yt/src/storage/migrate/mod.rs
+++ /dev/null
@@ -1,320 +0,0 @@
-// yt - A fully featured command line YouTube client
-//
-// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-// This file is part of Yt.
-//
-// You should have received a copy of the License along with this program.
-// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
-
-use std::{
-    fmt::Display,
-    future::Future,
-    time::{SystemTime, UNIX_EPOCH},
-};
-
-use anyhow::{Context, Result, bail};
-use chrono::TimeDelta;
-use log::{debug, info};
-use sqlx::{Sqlite, SqlitePool, Transaction, query};
-
-use crate::app::App;
-
-#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
-pub enum DbVersion {
-    /// The database is not yet initialized.
-    Empty,
-
-    /// The first database version.
-    /// Introduced: 2025-02-16.
-    Zero,
-
-    /// Introduced: 2025-02-17.
-    One,
-
-    /// Introduced: 2025-02-18.
-    Two,
-}
-const CURRENT_VERSION: DbVersion = DbVersion::Two;
-
-async fn add_error_context(
-    function: impl Future<Output = Result<()>>,
-    level: DbVersion,
-) -> Result<()> {
-    function
-        .await
-        .with_context(|| format!("Format failed to migrate database to version: {level}"))
-}
-
-async fn set_db_version(
-    tx: &mut Transaction<'_, Sqlite>,
-    old_version: Option<DbVersion>,
-    new_version: DbVersion,
-) -> Result<()> {
-    let valid_from = get_current_date();
-
-    if let Some(old_version) = old_version {
-        let valid_to = valid_from + 1;
-        let old_version = old_version.as_sql_integer();
-
-        query!(
-            "UPDATE version SET valid_to = ? WHERE namespace = 'yt' AND number = ?;",
-            valid_to,
-            old_version
-        )
-        .execute(&mut *(*tx))
-        .await?;
-    }
-
-    let version = new_version.as_sql_integer();
-
-    query!(
-        "INSERT INTO version (namespace, number, valid_from, valid_to) VALUES ('yt', ?, ?, NULL);",
-        version,
-        valid_from
-    )
-    .execute(&mut *(*tx))
-    .await?;
-
-    Ok(())
-}
-
-impl DbVersion {
-    fn as_sql_integer(self) -> i32 {
-        match self {
-            DbVersion::Empty => unreachable!("A empty version does not have an associated integer"),
-            DbVersion::Zero => 0,
-            DbVersion::One => 1,
-            DbVersion::Two => 2,
-        }
-    }
-    fn from_db(number: i64, namespace: &str) -> Result<Self> {
-        match (number, namespace) {
-            (0, "yt") => Ok(DbVersion::Zero),
-            (1, "yt") => Ok(DbVersion::One),
-            (2, "yt") => Ok(DbVersion::Two),
-
-            (0, other) => bail!("Db version is Zero, but got unknown namespace: '{other}'"),
-            (1, other) => bail!("Db version is One, but got unknown namespace: '{other}'"),
-            (2, other) => bail!("Db version is Two, but got unknown namespace: '{other}'"),
-
-            (other, "yt") => bail!("Got unkown version for 'yt' namespace: {other}"),
-            (num, nasp) => bail!("Got unkown version number ({num}) and namespace ('{nasp}')"),
-        }
-    }
-
-    /// Try to update the database from version [`self`] to the [`CURRENT_VERSION`].
-    ///
-    /// Each update is atomic, so if this function fails you are still guaranteed to have a
-    /// database at version `get_version`.
-    #[allow(clippy::too_many_lines)]
-    async fn update(self, app: &App) -> Result<()> {
-        match self {
-            DbVersion::Empty => {
-                add_error_context(
-                    async {
-                        let mut tx = app
-                            .database
-                            .begin()
-                            .await
-                            .context("Failed to start transaction")?;
-                        debug!("Migrate: Empty -> Zero");
-
-                        sqlx::raw_sql(include_str!("./sql/00_empty_to_zero.sql"))
-                            .execute(&mut *tx)
-                            .await
-                            .context("Failed to execute sql update script")?;
-
-                        set_db_version(&mut tx, None, DbVersion::Zero)
-                            .await
-                            .context("Failed to set new version")?;
-
-                        tx.commit()
-                            .await
-                            .context("Failed to commit changes")?;
-
-                        // NOTE: This is needed, so that sqlite "sees" our changes to the table
-                        // without having to reconnect. <2025-02-18>
-                        query!("VACUUM")
-                            .execute(&app.database)
-                            .await
-                            .context("Failed to vacuum database")?;
-
-                        Ok(())
-                    },
-                    DbVersion::One,
-                )
-                .await?;
-                Box::pin(Self::Zero.update(app)).await
-            }
-
-            DbVersion::Zero => {
-                add_error_context(
-                    async {
-                        let mut tx = app
-                            .database
-                            .begin()
-                            .await
-                            .context("Failed to start transaction")?;
-                        debug!("Migrate: Zero -> One");
-
-                        sqlx::raw_sql(include_str!("./sql/01_zero_to_one.sql"))
-                            .execute(&mut *tx)
-                            .await
-                            .context("Failed to execute the update sql script")?;
-
-                        set_db_version(&mut tx, Some(DbVersion::Zero), DbVersion::One)
-                            .await
-                            .context("Failed to set the new version")?;
-
-                        tx.commit()
-                            .await
-                            .context("Failed to commit the update transaction")?;
-
-                        // NOTE: This is needed, so that sqlite "sees" our changes to the table
-                        // without having to reconnect. <2025-02-18>
-                        query!("VACUUM")
-                            .execute(&app.database)
-                            .await
-                            .context("Failed to vacuum database")?;
-
-                        Ok(())
-                    },
-                    DbVersion::Zero,
-                )
-                .await?;
-
-                Box::pin(Self::One.update(app)).await
-            }
-
-            DbVersion::One => {
-                add_error_context(
-                    async {
-                        let mut tx = app
-                            .database
-                            .begin()
-                            .await
-                            .context("Failed to start the update transaction")?;
-                        debug!("Migrate: One -> Two");
-
-                        sqlx::raw_sql(include_str!("./sql/02_one_to_two.sql"))
-                            .execute(&mut *tx)
-                            .await
-                            .context("Failed to run the update sql script")?;
-
-                        set_db_version(&mut tx, Some(DbVersion::One), DbVersion::Two)
-                            .await
-                            .context("Failed to set the new version")?;
-
-                        tx.commit()
-                            .await
-                            .context("Failed to commit the update transaction")?;
-
-                        // NOTE: This is needed, so that sqlite "sees" our changes to the table
-                        // without having to reconnect. <2025-02-18>
-                        query!("VACUUM")
-                            .execute(&app.database)
-                            .await
-                            .context("Failed to vacuum database")?;
-
-                        Ok(())
-                    },
-                    DbVersion::One,
-                )
-                .await?;
-
-                Box::pin(Self::Two.update(app))
-                    .await
-                    .context("Failed to update to version: Three")
-            }
-
-            // This is the current_version
-            DbVersion::Two => {
-                assert_eq!(self, CURRENT_VERSION);
-                assert_eq!(self, get_version(app).await?);
-                Ok(())
-            }
-        }
-    }
-}
-impl Display for DbVersion {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        // It is a unit only enum, thus we can simply use the Debug formatting
-        <Self as std::fmt::Debug>::fmt(self, f)
-    }
-}
-
-/// Returns the current data as UNIX time stamp.
-fn get_current_date() -> i64 {
-    let start = SystemTime::now();
-    let seconds_since_epoch: TimeDelta = TimeDelta::from_std(
-        start
-            .duration_since(UNIX_EPOCH)
-            .expect("Time went backwards"),
-    )
-    .expect("Time does not go backwards");
-
-    // All database dates should be after the UNIX_EPOCH (and thus positiv)
-    seconds_since_epoch.num_milliseconds()
-}
-
-/// Return the current database version.
-///
-/// # Panics
-/// Only if internal assertions fail.
-pub async fn get_version(app: &App) -> Result<DbVersion> {
-    get_version_db(&app.database).await
-}
-/// Return the current database version.
-///
-/// In contrast to the [`get_version`] function, this function does not
-/// a fully instantiated [`App`], a database connection suffices.
-///
-/// # Panics
-/// Only if internal assertions fail.
-pub async fn get_version_db(pool: &SqlitePool) -> Result<DbVersion> {
-    let version_table_exists = {
-        let query = query!(
-            "SELECT 1 as result FROM sqlite_master WHERE type = 'table' AND name = 'version'"
-        )
-        .fetch_optional(pool)
-        .await?;
-        if let Some(output) = query {
-            assert_eq!(output.result, 1);
-            true
-        } else {
-            false
-        }
-    };
-    if !version_table_exists {
-        return Ok(DbVersion::Empty);
-    }
-
-    let current_version = query!(
-        "
-        SELECT namespace, number FROM version WHERE valid_to IS NULL;
-        "
-    )
-    .fetch_one(pool)
-    .await
-    .context("Failed to fetch version number")?;
-
-    DbVersion::from_db(current_version.number, current_version.namespace.as_str())
-}
-
-pub async fn migrate_db(app: &App) -> Result<()> {
-    let current_version = get_version(app)
-        .await
-        .context("Failed to determine initial version")?;
-
-    if current_version == CURRENT_VERSION {
-        return Ok(());
-    }
-
-    info!("Migrate database from version '{current_version}' to version '{CURRENT_VERSION}'");
-
-    current_version.update(app).await?;
-
-    Ok(())
-}