diff options
Diffstat (limited to 'crates/rocie-server/src/storage/migrate')
| -rw-r--r-- | crates/rocie-server/src/storage/migrate/defaults.rs | 99 | ||||
| -rw-r--r-- | crates/rocie-server/src/storage/migrate/mod.rs | 72 | ||||
| -rw-r--r-- | crates/rocie-server/src/storage/migrate/sql/0->1.sql | 7 |
3 files changed, 168 insertions, 10 deletions
diff --git a/crates/rocie-server/src/storage/migrate/defaults.rs b/crates/rocie-server/src/storage/migrate/defaults.rs new file mode 100644 index 0000000..3a2019c --- /dev/null +++ b/crates/rocie-server/src/storage/migrate/defaults.rs @@ -0,0 +1,99 @@ +use crate::{ + app::App, + storage::sql::{insert::Operations, unit::Unit, unit_property::UnitProperty}, +}; + +#[expect(clippy::unnecessary_wraps, reason = "The API expects an Option<_>")] +fn so(input: &'static str) -> Option<String> { + Some(s(input)) +} +fn s(input: &'static str) -> String { + String::from(input) +} + +macro_rules! register { + ( + $app:ident # $unit_prop_description:literal, $unit_prop_name:literal + $( + -> $unit_full_name_singular:ident, $unit_full_name_plural:ident, $unit_short_name:ident + )* + ) => { + let mut ops = Operations::new(concat!( + "create", + $unit_prop_name, + "unit property (during provisioning)" + )); + let unit_property = UnitProperty::register( + s($unit_prop_name), + so($unit_prop_description), + &mut ops, + ) + .id; + ops.apply(&$app).await?; + + let mut ops = Operations::new(concat!( + "create default units for", + $unit_prop_name, + "property (during provisioning)" + )); + + $( + Unit::register( + s(stringify!($unit_full_name_singular)), + s(stringify!($unit_full_name_plural)), + s(stringify!($unit_short_name)), + None, + unit_property, + &mut ops, + ); + )* + + ops.apply(&$app).await?; + }; +} + +pub(super) async fn add_defaults_0_to_1(app: &App) -> Result<(), apply_defaults::Error> { + register!( + app # "Time mesurement units", "Time" + -> second, seconds, s + -> minute, minutes, min + -> hour, hours, h + ); + + register!( + app # "Mass (weight) mesurement units", "Mass" + -> milligram, milligrams, mg + -> gram, grams, g + -> kilogram, kilograms, kg + ); + + register!( + app # "Volume mesurement units", "Volume" + -> milliliter, millilters, ml + -> deciliter, deciliters, dl + -> liter, liters, l + + // English + -> tablespoon, tablespoons, tbsp + -> teaspoon, teaspoons, tsp + + // Swedish + -> tesked, teskedar, tsk + -> matsked, matskedar, msk + ); + + Ok(()) +} + +pub(crate) mod apply_defaults { + use crate::storage::sql::insert::{self, apply}; + + #[derive(thiserror::Error, Debug)] + pub(crate) enum Error { + #[error("Failed to add new default unit-property the database: {0}")] + AddUnitProperty(#[from] apply::Error<insert::unit_property::Operation>), + + #[error("Failed to add new default unit the database: {0}")] + AddUnit(#[from] apply::Error<insert::unit::Operation>), + } +} diff --git a/crates/rocie-server/src/storage/migrate/mod.rs b/crates/rocie-server/src/storage/migrate/mod.rs index ae0732b..5c81580 100644 --- a/crates/rocie-server/src/storage/migrate/mod.rs +++ b/crates/rocie-server/src/storage/migrate/mod.rs @@ -7,10 +7,15 @@ use chrono::TimeDelta; use log::{debug, info}; use sqlx::{Sqlite, SqlitePool, Transaction, query}; -use crate::app::App; +use crate::{ + app::App, + storage::sql::{config::Config, get::config}, +}; + +mod defaults; macro_rules! make_upgrade { - ($app:expr, $old_version:expr, $new_version:expr, $sql_name:expr) => { + ($app:expr, $old_version:expr, $new_version:expr, $sql_name:expr) => {{ let mut tx = $app .db .begin() @@ -57,8 +62,8 @@ macro_rules! make_upgrade { new_version: $new_version, })?; - Ok(()) - }; + Ok::<_, update::Error>(()) + }}; } #[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] @@ -135,11 +140,12 @@ impl DbVersion { /// /// 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<(), update::Error> { match self { Self::Empty => { - make_upgrade! {app, Self::Empty, Self::One, "./sql/0->1.sql"} + make_upgrade! {app, Self::Empty, Self::One, "./sql/0->1.sql"}?; + + Ok(()) } // This is the current_version @@ -158,7 +164,10 @@ impl Display for DbVersion { } } pub(crate) mod update { - use crate::storage::migrate::{DbVersion, db_version_set, get_db_version}; + use crate::storage::{ + migrate::{DbVersion, db_version_set, defaults, get_db_version}, + sql::get::config, + }; #[derive(thiserror::Error, Debug)] pub(crate) enum Error { @@ -182,6 +191,11 @@ pub(crate) mod update { #[error("Failed to commit the update transaction: {0}")] TxnCommit(sqlx::Error), + #[error("Failed to access the rocie config: {0}")] + ConfigGet(#[from] config::get::Error), + #[error("Failed to add defaults to the database: {0}")] + AddDefaults(#[from] defaults::apply_defaults::Error), + #[error("Failed to perform the next chained update (to ver {new_version}): {err}")] NextUpdate { err: Box<Self>, @@ -197,6 +211,12 @@ pub(crate) mod db_version_parse { } } +async fn should_use_defaults(app: &App) -> Result<bool, config::get::Error> { + let config = Config::get(app).await?; + + Ok(config.should_use_defaults) +} + /// Returns the current data as UNIX time stamp. pub(crate) fn get_current_date() -> i64 { let start = SystemTime::now(); @@ -238,7 +258,7 @@ pub(crate) async fn get_version_db(pool: &SqlitePool) -> Result<DbVersion, get_d ) .fetch_optional(pool) .await - .map_err(|err| get_db_version::Error::VersionTableExistance(err))?; + .map_err(get_db_version::Error::VersionTableExistance)?; if let Some(output) = query { assert_eq!(output.result, 1); @@ -261,7 +281,7 @@ pub(crate) async fn get_version_db(pool: &SqlitePool) -> Result<DbVersion, get_d ) .fetch_one(pool) .await - .map_err(|err| get_db_version::Error::VersionNumberFetch(err))?; + .map_err(get_db_version::Error::VersionNumberFetch)?; Ok(DbVersion::from_db( current_version.number, @@ -288,6 +308,26 @@ pub(crate) mod get_db_version { pub(crate) async fn migrate_db(app: &App) -> Result<(), migrate_db::Error> { let current_version = get_version(app).await?; + if app.db_version_at_start.get().is_none() { + app.db_version_at_start + .set(current_version) + .expect("the cell to be unititialized, we checked"); + } + + if app.db_version_at_start.get() == Some(&DbVersion::Empty) { + // We cannot run this code in the normal update function, because for the empty db, there + // is no way to know if we want defaults. + // + // So we need to add the defaults later, which is achieved by another call to `migrate_db` + // in provision. + // + // That is kinda hacky, but I don't see a way around it. + // For defaults added in version Two and onward, the default mechanism should just work. + if should_use_defaults(app).await? { + defaults::add_defaults_0_to_1(app).await?; + } + } + if current_version == CURRENT_VERSION { return Ok(()); } @@ -300,7 +340,12 @@ pub(crate) async fn migrate_db(app: &App) -> Result<(), migrate_db::Error> { } pub(crate) mod migrate_db { - use crate::storage::migrate::{get_db_version, update}; + use actix_web::ResponseError; + + use crate::storage::{ + migrate::{defaults, get_db_version, update}, + sql::get::config, + }; #[derive(thiserror::Error, Debug)] pub(crate) enum Error { @@ -309,5 +354,12 @@ pub(crate) mod migrate_db { #[error("Failed to update the database: {0}")] Upadate(#[from] update::Error), + + #[error("Failed to access the rocie config: {0}")] + ConfigGet(#[from] config::get::Error), + #[error("Failed to add defaults to the database: {0}")] + AddDefaults(#[from] defaults::apply_defaults::Error), } + + impl ResponseError for Error {} } diff --git a/crates/rocie-server/src/storage/migrate/sql/0->1.sql b/crates/rocie-server/src/storage/migrate/sql/0->1.sql index ba44c68..8f99322 100644 --- a/crates/rocie-server/src/storage/migrate/sql/0->1.sql +++ b/crates/rocie-server/src/storage/migrate/sql/0->1.sql @@ -178,3 +178,10 @@ CREATE TABLE txn_log ( timestamp INTEGER NOT NULL, operation TEXT NOT NULL ) STRICT; + +CREATE TABLE config ( + -- Make it impossible to insert more than one value here. + id INTEGER PRIMARY KEY NOT NULL CHECK (id = 0), + use_defaults INTEGER NOT NULL CHECK (use_defaults = 1 OR use_defaults = 0) +) STRICT; +INSERT INTO config (id, use_defaults) VALUES (0, 0); |
