#![allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)] use poise::serenity_prelude::{ChannelId, GuildId}; use sqlx::{migrate::MigrateDatabase, Sqlite, SqlitePool}; use tracing::{error, info, debug}; pub type Result = ::std::result::Result; pub struct Database(SqlitePool); impl Database { /// Initialize Sqlite database. /// /// The Sqlite database should already exist and have its /// migrations already executed. /// /// # Errors /// /// This function will return an error if the Sqlite pool fails to /// create. pub async fn new() -> Result { let url = "sqlite:p4bl0t.db"; if !Sqlite::database_exists(url).await? { info!("Creating database"); Sqlite::create_database(url).await?; info!("Database created"); } debug!("Getting pool connection"); let pool = SqlitePool::connect(url).await?; info!("Running migrations"); sqlx::migrate!().run(&pool).await?; debug!("Database initialized"); Ok(Self(pool)) } /// Return from database all channels registered as loggers for a /// guild. /// /// # Errors /// /// This function will return an error if `sqlx` does so. pub async fn get_logging_channels( &self, guild_id: GuildId, ) -> Result> { let guild_id = guild_id.0 as i64; sqlx::query!( r#" SELECT channel_id FROM guild_log_channels WHERE guild_id = ?1"#, guild_id ) .fetch_all(&self.0) .await .map_err(|e| { error!( "Error getting logging channels for guild {guild_id}: {e:?}" ); e }) .map(|channels| { channels .iter() .map(|id| ChannelId(id.channel_id as u64)) .collect() }) } /// Adds a channel as a logger for a guild. /// /// # Errors /// /// This function will return an error if `sqlx` does so. This may /// be either a database issue, or a channel is already registered /// as a guild's logger, therefore violating the unicity /// constraint for guild ID and channel ID pairs. pub async fn set_logging_channel( &self, guild_id: GuildId, channel_id: ChannelId, ) -> Result<()> { let guild_id = guild_id.0 as i64; let channel_id = channel_id.0 as i64; let mut conn = self.0.acquire().await?; sqlx::query!(r#" INSERT INTO guild_log_channels (guild_id, channel_id) VALUES ( ?1, ?2 )"#, guild_id, channel_id ) .execute(&mut *conn) .await .map_err(|e| { error!("Error setting channel {channel_id} as logger for guild {guild_id}: {e:?}"); e }) .map(|_| ()) } /// Unregister a channel as a logger for a guild. /// /// This function will return a success value even if `channel` /// was not a logger of `guild` already. /// /// # Errors /// /// This function will return an error if `sqlx` does so. pub async fn remove_logging_channel( &self, guild: GuildId, channel: ChannelId, ) -> Result<()> { let guild_id = guild.0 as i64; let channel_id = channel.0 as i64; let mut conn = self.0.acquire().await?; sqlx::query!(r#" DELETE FROM guild_log_channels WHERE guild_id = ?1 AND channel_id = ?2"#, guild_id, channel_id) .execute(&mut *conn) .await .map_err(|e| { error!("Error removing channel {channel_id} as a logger for guild {guild_id}: {e:?}"); e }) .map(|_| ()) } }