feat: starting db abstractions

user-register
Jef Roosens 2023-05-16 15:47:13 +02:00
parent 303e3ffd4e
commit 0b10885015
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
6 changed files with 119 additions and 44 deletions

View File

@ -1,7 +1,11 @@
-- Your SQL goes here -- Your SQL goes here
CREATE TABLE users ( CREATE TABLE users (
discord_id UNSIGNED BIG INT PRIMARY KEY NOT NULL, id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
discord_id UNSIGNED BIG INT NOT NULL,
guild_id UNSIGNED BIG INT NOT NULL,
email TEXT UNIQUE NOT NULL, email TEXT UNIQUE NOT NULL,
first_name TEXT NOT NULL, first_name TEXT NOT NULL,
last_name TEXT NOT NULL last_name TEXT NOT NULL,
UNIQUE(discord_id, guild_id)
); );

View File

@ -1,5 +1,6 @@
use crate::db::users::{user_all, user_insert, User}; use crate::db::users::{User, NewUser};
use crate::{Context, Error}; use crate::{Context, Error};
use diesel::RunQueryDsl;
#[poise::command(prefix_command, slash_command)] #[poise::command(prefix_command, slash_command)]
pub async fn register( pub async fn register(
@ -8,16 +9,21 @@ pub async fn register(
last_name: String, last_name: String,
email: String, email: String,
) -> Result<(), Error> { ) -> Result<(), Error> {
let user = User { if let Some(guild_id) = ctx.guild_id() {
discord_id: ctx.author().id.0 as i64, let new_user = NewUser {
first_name, discord_id: ctx.author().id.0 as i64,
last_name, guild_id: guild_id.into(),
email, first_name,
}; last_name,
email,
};
{ {
let mut conn = ctx.data().pool.get()?; let mut conn = ctx.data().pool.get()?;
user_insert(&mut conn, &user); new_user.insert(&mut conn);
}
} else {
ctx.say("You have to send this message from a guild.").await?;
} }
Ok(()) Ok(())
@ -25,21 +31,25 @@ pub async fn register(
#[poise::command(prefix_command, slash_command)] #[poise::command(prefix_command, slash_command)]
pub async fn registered(ctx: Context<'_>) -> Result<(), Error> { pub async fn registered(ctx: Context<'_>) -> Result<(), Error> {
let users = { if let Some(guild_id) = ctx.guild_id() {
let mut conn = ctx.data().pool.get()?; let users = {
user_all(&mut conn) let mut conn = ctx.data().pool.get()?;
}; User::by_guild_id(guild_id.into()).load(&mut conn)?
};
ctx.send(|f| { ctx.send(|f| {
f.embed(|e| { f.embed(|e| {
e.description("Registered users").fields( e.description("Registered users").fields(
users users
.into_iter() .into_iter()
.map(|u| (format!("{} {}", u.first_name, u.last_name), u.email, false)), .map(|u| (format!("{} {}", u.first_name, u.last_name), u.email, false)),
) )
})
}) })
}) .await?;
.await?; } else {
ctx.say("You are not in a guild.").await?;
}
Ok(()) Ok(())
} }

View File

@ -1,2 +1,23 @@
mod schema; mod schema;
pub mod users; pub mod users;
use diesel::sqlite::SqliteConnection;
use diesel::connection::SimpleConnection;
use diesel::QueryResult;
use diesel::r2d2::{ConnectionManager, Pool};
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;")
}
pub fn initialize_pool(url: &str) -> Pool<ConnectionManager<SqliteConnection>> {
let manager = ConnectionManager::new(url);
let pool = Pool::builder().test_on_check_out(true).build(manager).expect("oops");
let mut conn = pool.get().unwrap();
initialize_db(&mut conn).unwrap();
pool
}

View File

@ -1,8 +1,10 @@
// @generated automatically by Diesel CLI. // @generated automatically by Diesel CLI.
diesel::table! { diesel::table! {
users (discord_id) { users (id) {
id -> Integer,
discord_id -> BigInt, discord_id -> BigInt,
guild_id -> BigInt,
email -> Text, email -> Text,
first_name -> Text, first_name -> Text,
last_name -> Text, last_name -> Text,

View File

@ -1,22 +1,68 @@
use super::schema::users; use super::schema::users::{self, dsl::*};
use diesel::prelude::*; use diesel::prelude::*;
use diesel::sqlite::SqliteConnection; 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::expression::AsExpression;
#[derive(Queryable, Insertable)] #[derive(Queryable, Selectable)]
#[diesel(table_name = users)]
pub struct User { pub struct User {
pub id: i32,
pub discord_id: i64, pub discord_id: i64,
pub guild_id: i64,
pub email: String, pub email: String,
pub first_name: String, pub first_name: String,
pub last_name: String, pub last_name: String,
} }
pub fn user_insert(conn: &mut SqliteConnection, user: &User) -> User { #[derive(Insertable)]
diesel::insert_into(users::table) #[diesel(table_name = users)]
.values(user) pub struct NewUser {
.get_result(conn) pub discord_id: i64,
.expect("fuck") pub guild_id: i64,
pub email: String,
pub first_name: String,
pub last_name: String,
} }
pub fn user_all(conn: &mut SqliteConnection) -> Vec<User> { type All = Select<users::table, AsSelect<User, Sqlite>>;
users::table.load::<User>(conn).expect("nou") type WithGuild<T> = Eq<guild_id, T>;
type ByGuild<T> = Filter<All, WithGuild<T>>;
impl User {
pub fn all() -> All {
users::table.select(User::as_select())
}
// pub fn by_guild<T>(guild_id_: T) -> ByGuild<T>
// where T: AsExpression<BigInt>
// {
// Self::all().filter(guild_id.eq(guild_id_))
// }
pub fn by_guild_id(guild_id_: i64) -> ByGuild<i64>
{
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> {
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> {
Self::all().filter(id.eq(id_)).first(conn)
}
}
impl NewUser {
pub fn insert(&self, conn: &mut SqliteConnection) -> User {
diesel::insert_into(users::table)
.values(self)
.get_result(conn)
.expect("fuck")
}
} }

View File

@ -2,9 +2,7 @@ mod commands;
mod db; mod db;
use affluences_api::AffluencesClient; use affluences_api::AffluencesClient;
use diesel::Connection;
use poise::serenity_prelude as serenity; use poise::serenity_prelude as serenity;
use std::sync::Mutex;
use std::{env::var, time::Duration}; use std::{env::var, time::Duration};
use diesel::r2d2::{ConnectionManager, Pool}; use diesel::r2d2::{ConnectionManager, Pool};
use diesel::sqlite::SqliteConnection; use diesel::sqlite::SqliteConnection;
@ -36,12 +34,6 @@ async fn on_error(error: poise::FrameworkError<'_, Data, Error>) {
} }
} }
fn db_connection_pool(url: &str) -> Pool<ConnectionManager<SqliteConnection>> {
let manager = ConnectionManager::new(url);
Pool::builder().test_on_check_out(true).build(manager).expect("oops")
}
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
// FrameworkOptions contains all of poise's configuration option in one struct // FrameworkOptions contains all of poise's configuration option in one struct
@ -103,7 +95,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_connection_pool("affy.db") pool: db::initialize_pool("affy.db")
}) })
}) })
}) })