From 04e268a17cfc481e5d1f729814dfb21f90864c21 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Tue, 16 May 2023 16:25:33 +0200 Subject: [PATCH] feat: database migrations --- .gitignore | 2 +- Cargo.lock | 42 ++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + migrations/.keep | 0 src/build.rs | 3 +++ src/commands/users.rs | 7 +++++-- src/db/mod.rs | 43 +++++++++++++++++++++++++++++++++---------- src/db/users.rs | 28 +++++++++++++++++----------- src/main.rs | 10 ++++++---- 9 files changed, 108 insertions(+), 28 deletions(-) delete mode 100644 migrations/.keep create mode 100644 src/build.rs diff --git a/.gitignore b/.gitignore index a7e99c9..ac95708 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,4 @@ rust-project.json # End of https://www.toptal.com/developers/gitignore/api/rust,rust-analyzer -*.db +*.db* diff --git a/Cargo.lock b/Cargo.lock index c9d2c0a..b8388a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -36,6 +36,7 @@ dependencies = [ "async-minecraft-ping", "chrono", "diesel", + "diesel_migrations", "poise", "tokio", "uuid", @@ -437,6 +438,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "diesel_migrations" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9ae22beef5e9d6fab9225ddb073c1c6c1a7a6ded5019d5da11d1e5c5adc34e2" +dependencies = [ + "diesel", + "migrations_internals", + "migrations_macros", +] + [[package]] name = "digest" version = "0.10.6" @@ -867,6 +879,27 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "migrations_internals" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c493c09323068c01e54c685f7da41a9ccf9219735c3766fbfd6099806ea08fbc" +dependencies = [ + "serde", + "toml", +] + +[[package]] +name = "migrations_macros" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a8ff27a350511de30cdabb77147501c36ef02e0451d957abea2f30caffb2b58" +dependencies = [ + "migrations_internals", + "proc-macro2", + "quote", +] + [[package]] name = "mime" version = "0.3.17" @@ -1575,6 +1608,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + [[package]] name = "tower-service" version = "0.3.2" diff --git a/Cargo.toml b/Cargo.toml index 1faac2a..cde15dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,3 +23,4 @@ uuid = "*" poise = "0.5.5" async-minecraft-ping = "0.8.0" diesel = { version = "2.0.4", features = ["sqlite", "returning_clauses_for_sqlite_3_35", "r2d2"] } +diesel_migrations = { version = "2.0.0", features = [ "sqlite" ] } diff --git a/migrations/.keep b/migrations/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/src/build.rs b/src/build.rs new file mode 100644 index 0000000..508b53c --- /dev/null +++ b/src/build.rs @@ -0,0 +1,3 @@ +fn main() { + println!("cargo:rerun-if-changed=migrations"); +} diff --git a/src/commands/users.rs b/src/commands/users.rs index e692b6a..da968f6 100644 --- a/src/commands/users.rs +++ b/src/commands/users.rs @@ -1,4 +1,4 @@ -use crate::db::users::{User, NewUser}; +use crate::db::users::{NewUser, User}; use crate::{Context, Error}; use diesel::RunQueryDsl; @@ -22,8 +22,11 @@ pub async fn register( let mut conn = ctx.data().pool.get()?; new_user.insert(&mut conn); } + + ctx.say("You have been registered.").await?; } else { - ctx.say("You have to send this message from a guild.").await?; + ctx.say("You have to send this message from a guild.") + .await?; } Ok(()) diff --git a/src/db/mod.rs b/src/db/mod.rs index 6c29d3a..1542bea 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1,23 +1,46 @@ mod schema; pub mod users; -use diesel::sqlite::SqliteConnection; use diesel::connection::SimpleConnection; -use diesel::QueryResult; use diesel::r2d2::{ConnectionManager, Pool}; +use diesel::sqlite::{Sqlite, SqliteConnection}; +use std::error::Error; -fn initialize_db(conn: &mut SqliteConnection) -> QueryResult<()> { - // Enable WAL mode and enforce foreign keys - conn.batch_execute("PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL; PRAGMA foreign_keys = ON;") +use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness}; +pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations"); + +type DbError = Box; + +fn run_migrations(connection: &mut impl MigrationHarness) -> Result<(), DbError> { + // This will run the necessary migrations. + // + // See the documentation for `MigrationHarness` for + // all available methods. + connection.run_pending_migrations(MIGRATIONS)?; + + Ok(()) } -pub fn initialize_pool(url: &str) -> Pool> { +fn initialize_db(conn: &mut SqliteConnection) -> Result<(), DbError> { + // Enable WAL mode and enforce foreign keys + conn.batch_execute( + "PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL; PRAGMA foreign_keys = ON;", + )?; + run_migrations(conn)?; + + Ok(()) +} + +pub fn initialize_pool(url: &str) -> Result>, DbError> { let manager = ConnectionManager::new(url); - let pool = Pool::builder().test_on_check_out(true).build(manager).expect("oops"); + let pool = Pool::builder() + .test_on_check_out(true) + .build(manager) + .expect("oops"); - let mut conn = pool.get().unwrap(); - initialize_db(&mut conn).unwrap(); + let mut conn = pool.get()?; + initialize_db(&mut conn)?; - pool + Ok(pool) } diff --git a/src/db/users.rs b/src/db/users.rs index 357ff3b..09360ee 100644 --- a/src/db/users.rs +++ b/src/db/users.rs @@ -1,12 +1,12 @@ use super::schema::users::{self, dsl::*}; -use diesel::prelude::*; -use diesel::sqlite::SqliteConnection; -use diesel::dsl::{AsSelect, Select}; -use diesel::sqlite::Sqlite; use diesel::dsl::Eq; -use diesel::helper_types::Filter; -use diesel::sql_types::BigInt; +use diesel::dsl::{AsSelect, Select}; use diesel::expression::AsExpression; +use diesel::helper_types::Filter; +use diesel::prelude::*; +use diesel::sql_types::BigInt; +use diesel::sqlite::Sqlite; +use diesel::sqlite::SqliteConnection; #[derive(Queryable, Selectable)] #[diesel(table_name = users)] @@ -44,13 +44,19 @@ impl User { // Self::all().filter(guild_id.eq(guild_id_)) // } - pub fn by_guild_id(guild_id_: i64) -> ByGuild - { + pub fn by_guild_id(guild_id_: i64) -> ByGuild { Self::all().filter(guild_id.eq(guild_id_)) } - - pub fn get(conn: &mut SqliteConnection, guild_id_: i64, discord_id_: i64) -> Result { - Self::all().filter(guild_id.eq(guild_id_)).filter(discord_id.eq(discord_id_)).first(conn) + + pub fn get( + conn: &mut SqliteConnection, + guild_id_: i64, + discord_id_: i64, + ) -> Result { + Self::all() + .filter(guild_id.eq(guild_id_)) + .filter(discord_id.eq(discord_id_)) + .first(conn) } pub fn get_by_id(conn: &mut SqliteConnection, id_: i32) -> Result { diff --git a/src/main.rs b/src/main.rs index c0a9ae7..c1457cf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,10 +2,10 @@ mod commands; mod db; use affluences_api::AffluencesClient; -use poise::serenity_prelude as serenity; -use std::{env::var, time::Duration}; use diesel::r2d2::{ConnectionManager, Pool}; use diesel::sqlite::SqliteConnection; +use poise::serenity_prelude as serenity; +use std::{env::var, time::Duration}; // Types used by all command functions type Error = Box; @@ -14,7 +14,7 @@ type Context<'a> = poise::Context<'a, Data, Error>; // Custom user data passed to all command functions pub struct Data { client: AffluencesClient, - pool: Pool> + pool: Pool>, } async fn on_error(error: poise::FrameworkError<'_, Data, Error>) { @@ -84,6 +84,8 @@ async fn main() { ..Default::default() }; + let pool = db::initialize_pool("affy.db").unwrap(); + poise::Framework::builder() .token( var("DISCORD_TOKEN") @@ -95,7 +97,7 @@ async fn main() { poise::builtins::register_globally(ctx, &framework.options().commands).await?; Ok(Data { client: AffluencesClient::new(), - pool: db::initialize_pool("affy.db") + pool, }) }) })