feat: database migrations
							parent
							
								
									0b10885015
								
							
						
					
					
						commit
						04e268a17c
					
				|  | @ -24,4 +24,4 @@ rust-project.json | ||||||
| 
 | 
 | ||||||
| # End of https://www.toptal.com/developers/gitignore/api/rust,rust-analyzer | # End of https://www.toptal.com/developers/gitignore/api/rust,rust-analyzer | ||||||
| 
 | 
 | ||||||
| *.db | *.db* | ||||||
|  |  | ||||||
|  | @ -36,6 +36,7 @@ dependencies = [ | ||||||
|  "async-minecraft-ping", |  "async-minecraft-ping", | ||||||
|  "chrono", |  "chrono", | ||||||
|  "diesel", |  "diesel", | ||||||
|  |  "diesel_migrations", | ||||||
|  "poise", |  "poise", | ||||||
|  "tokio", |  "tokio", | ||||||
|  "uuid", |  "uuid", | ||||||
|  | @ -437,6 +438,17 @@ dependencies = [ | ||||||
|  "syn 1.0.109", |  "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]] | [[package]] | ||||||
| name = "digest" | name = "digest" | ||||||
| version = "0.10.6" | version = "0.10.6" | ||||||
|  | @ -867,6 +879,27 @@ version = "2.5.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" | 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]] | [[package]] | ||||||
| name = "mime" | name = "mime" | ||||||
| version = "0.3.17" | version = "0.3.17" | ||||||
|  | @ -1575,6 +1608,15 @@ dependencies = [ | ||||||
|  "tracing", |  "tracing", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "toml" | ||||||
|  | version = "0.5.11" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" | ||||||
|  | dependencies = [ | ||||||
|  |  "serde", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "tower-service" | name = "tower-service" | ||||||
| version = "0.3.2" | version = "0.3.2" | ||||||
|  |  | ||||||
|  | @ -23,3 +23,4 @@ uuid = "*" | ||||||
| poise = "0.5.5" | poise = "0.5.5" | ||||||
| async-minecraft-ping = "0.8.0" | async-minecraft-ping = "0.8.0" | ||||||
| diesel = { version = "2.0.4", features = ["sqlite", "returning_clauses_for_sqlite_3_35", "r2d2"] } | diesel = { version = "2.0.4", features = ["sqlite", "returning_clauses_for_sqlite_3_35", "r2d2"] } | ||||||
|  | diesel_migrations = { version = "2.0.0", features = [ "sqlite" ] } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fn main() { | ||||||
|  |    println!("cargo:rerun-if-changed=migrations"); | ||||||
|  | } | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| use crate::db::users::{User, NewUser}; | use crate::db::users::{NewUser, User}; | ||||||
| use crate::{Context, Error}; | use crate::{Context, Error}; | ||||||
| use diesel::RunQueryDsl; | use diesel::RunQueryDsl; | ||||||
| 
 | 
 | ||||||
|  | @ -22,8 +22,11 @@ pub async fn register( | ||||||
|             let mut conn = ctx.data().pool.get()?; |             let mut conn = ctx.data().pool.get()?; | ||||||
|             new_user.insert(&mut conn); |             new_user.insert(&mut conn); | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         ctx.say("You have been registered.").await?; | ||||||
|     } else { |     } 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(()) |     Ok(()) | ||||||
|  |  | ||||||
|  | @ -1,23 +1,46 @@ | ||||||
| mod schema; | mod schema; | ||||||
| pub mod users; | pub mod users; | ||||||
| 
 | 
 | ||||||
| use diesel::sqlite::SqliteConnection; |  | ||||||
| use diesel::connection::SimpleConnection; | use diesel::connection::SimpleConnection; | ||||||
| use diesel::QueryResult; |  | ||||||
| use diesel::r2d2::{ConnectionManager, Pool}; | use diesel::r2d2::{ConnectionManager, Pool}; | ||||||
|  | use diesel::sqlite::{Sqlite, SqliteConnection}; | ||||||
|  | use std::error::Error; | ||||||
| 
 | 
 | ||||||
| fn initialize_db(conn: &mut SqliteConnection) -> QueryResult<()> { | use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness}; | ||||||
|     // Enable WAL mode and enforce foreign keys
 | pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations"); | ||||||
|     conn.batch_execute("PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL; PRAGMA foreign_keys = ON;") | 
 | ||||||
|  | type DbError = Box<dyn Error + Send + Sync + 'static>; | ||||||
|  | 
 | ||||||
|  | fn run_migrations(connection: &mut impl MigrationHarness<Sqlite>) -> 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<ConnectionManager<SqliteConnection>> { | 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<Pool<ConnectionManager<SqliteConnection>>, DbError> { | ||||||
|     let manager = ConnectionManager::new(url); |     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(); |     let mut conn = pool.get()?; | ||||||
|     initialize_db(&mut conn).unwrap(); |     initialize_db(&mut conn)?; | ||||||
| 
 | 
 | ||||||
|     pool |     Ok(pool) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,12 +1,12 @@ | ||||||
| use super::schema::users::{self, dsl::*}; | 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::dsl::Eq; | ||||||
| use diesel::helper_types::Filter; | use diesel::dsl::{AsSelect, Select}; | ||||||
| use diesel::sql_types::BigInt; |  | ||||||
| use diesel::expression::AsExpression; | 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)] | #[derive(Queryable, Selectable)] | ||||||
| #[diesel(table_name = users)] | #[diesel(table_name = users)] | ||||||
|  | @ -44,13 +44,19 @@ impl User { | ||||||
|     //     Self::all().filter(guild_id.eq(guild_id_))
 |     //     Self::all().filter(guild_id.eq(guild_id_))
 | ||||||
|     // }
 |     // }
 | ||||||
| 
 | 
 | ||||||
|     pub fn by_guild_id(guild_id_: i64) -> ByGuild<i64> |     pub fn by_guild_id(guild_id_: i64) -> ByGuild<i64> { | ||||||
|      { |  | ||||||
|         Self::all().filter(guild_id.eq(guild_id_)) |         Self::all().filter(guild_id.eq(guild_id_)) | ||||||
|     } |     } | ||||||
|  
 | 
 | ||||||
|     pub fn get(conn: &mut SqliteConnection, guild_id_: i64, discord_id_: i64) -> Result<User, diesel::result::Error> { |     pub fn get( | ||||||
|         Self::all().filter(guild_id.eq(guild_id_)).filter(discord_id.eq(discord_id_)).first(conn) |         conn: &mut SqliteConnection, | ||||||
|  |         guild_id_: i64, | ||||||
|  |         discord_id_: i64, | ||||||
|  |     ) -> Result<User, diesel::result::Error> { | ||||||
|  |         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<User, diesel::result::Error> { |     pub fn get_by_id(conn: &mut SqliteConnection, id_: i32) -> Result<User, diesel::result::Error> { | ||||||
|  |  | ||||||
							
								
								
									
										10
									
								
								src/main.rs
								
								
								
								
							
							
						
						
									
										10
									
								
								src/main.rs
								
								
								
								
							|  | @ -2,10 +2,10 @@ mod commands; | ||||||
| mod db; | mod db; | ||||||
| 
 | 
 | ||||||
| use affluences_api::AffluencesClient; | use affluences_api::AffluencesClient; | ||||||
| use poise::serenity_prelude as serenity; |  | ||||||
| use std::{env::var, time::Duration}; |  | ||||||
| use diesel::r2d2::{ConnectionManager, Pool}; | use diesel::r2d2::{ConnectionManager, Pool}; | ||||||
| use diesel::sqlite::SqliteConnection; | use diesel::sqlite::SqliteConnection; | ||||||
|  | use poise::serenity_prelude as serenity; | ||||||
|  | use std::{env::var, time::Duration}; | ||||||
| 
 | 
 | ||||||
| // Types used by all command functions
 | // Types used by all command functions
 | ||||||
| type Error = Box<dyn std::error::Error + Send + Sync>; | type Error = Box<dyn std::error::Error + Send + Sync>; | ||||||
|  | @ -14,7 +14,7 @@ type Context<'a> = poise::Context<'a, Data, Error>; | ||||||
| // Custom user data passed to all command functions
 | // Custom user data passed to all command functions
 | ||||||
| pub struct Data { | pub struct Data { | ||||||
|     client: AffluencesClient, |     client: AffluencesClient, | ||||||
|     pool: Pool<ConnectionManager<SqliteConnection>> |     pool: Pool<ConnectionManager<SqliteConnection>>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async fn on_error(error: poise::FrameworkError<'_, Data, Error>) { | async fn on_error(error: poise::FrameworkError<'_, Data, Error>) { | ||||||
|  | @ -84,6 +84,8 @@ async fn main() { | ||||||
|         ..Default::default() |         ..Default::default() | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     let pool = db::initialize_pool("affy.db").unwrap(); | ||||||
|  | 
 | ||||||
|     poise::Framework::builder() |     poise::Framework::builder() | ||||||
|         .token( |         .token( | ||||||
|             var("DISCORD_TOKEN") |             var("DISCORD_TOKEN") | ||||||
|  | @ -95,7 +97,7 @@ async fn main() { | ||||||
|                 poise::builtins::register_globally(ctx, &framework.options().commands).await?; |                 poise::builtins::register_globally(ctx, &framework.options().commands).await?; | ||||||
|                 Ok(Data { |                 Ok(Data { | ||||||
|                     client: AffluencesClient::new(), |                     client: AffluencesClient::new(), | ||||||
|                     pool: db::initialize_pool("affy.db") |                     pool, | ||||||
|                 }) |                 }) | ||||||
|             }) |             }) | ||||||
|         }) |         }) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue