Files
siren/src/data/mod.rs

139 lines
4.3 KiB
Rust

use std::{fmt, sync::OnceLock, time::Duration};
use std::fmt::Display;
use chrono::{DateTime, Utc};
use redis::{aio::MultiplexedConnection as RedisConnection, Client as RedisClient, RedisResult};
use sqlx::{postgres::PgPoolOptions, Pool, Postgres};
use crate::error::SirenResult;
pub mod condition;
pub mod events;
mod executable_query;
pub mod guilds;
pub mod insert;
pub mod messages;
pub mod query;
pub mod update;
pub use executable_query::ExecutableQuery;
static POOL: OnceLock<Pool<Postgres>> = OnceLock::new();
static REDIS: OnceLock<RedisClient> = OnceLock::new();
pub async fn initialize() -> SirenResult<()> {
log::info!("Initializing database...");
let db_user = std::env::var("DATABASE_USER").unwrap_or("siren".to_string());
let db_password = std::env::var("DATABASE_PASSWORD").expect("DATABASE_PASSWORD must be set");
let db_host: String = std::env::var("DATABASE_HOST").expect("DATABASE_HOST must be set");
let db_port = std::env::var("DATABASE_PORT").unwrap_or("5432".to_string());
let db_name = std::env::var("DATABASE_NAME").unwrap_or("siren".to_string());
// Setup Postgres pool connection
let pool = PgPoolOptions::new()
.max_connections(5)
.acquire_timeout(Duration::from_secs(30))
.connect(&format!(
"postgres://{}:{}@{}:{}/{}",
db_user, db_password, db_host, db_port, db_name
))
.await?;
match POOL.set(pool) {
Ok(_) => {}
Err(_) => {
log::warn!("Database pool already initialized");
}
}
// Setup Redis connection
let redis = {
let host = std::env::var("REDIS_HOST").unwrap_or("localhost".to_string());
let port = std::env::var("REDIS_PORT").unwrap_or("6379".to_string());
let url = format!("redis://{}:{}", host, port);
RedisClient::open(url).expect("Failed to create redis client")
};
match REDIS.set(redis) {
Ok(_) => {}
Err(_) => {
log::warn!("Redis client already initialized");
}
}
// Run migrations
match run_migrations().await {
Ok(_) => log::debug!("Successfully ran migrations"),
Err(e) => log::error!("Failed to run migrations: {}", e),
}
log::info!("Database initialized");
Ok(())
}
pub fn pool() -> &'static Pool<Postgres> {
POOL.get().unwrap()
}
fn redis() -> &'static RedisClient {
REDIS.get().unwrap()
}
pub fn redis_connection() -> RedisResult<redis::Connection> {
let conn = redis().get_connection()?;
Ok(conn)
}
pub async fn redis_async_connection() -> RedisResult<RedisConnection> {
let conn = redis().get_multiplexed_async_connection().await?;
Ok(conn)
}
async fn run_migrations() -> SirenResult<()> {
log::debug!("Running migrations");
let pool = pool();
sqlx::migrate!().run(pool).await?;
Ok(())
}
#[derive(Debug, Clone)]
pub enum Value {
Int(i32),
OptionalInt(Option<i32>),
BigInt(i64),
OptionalBigInt(Option<i64>),
Float(f32),
OptionalFloat(Option<f32>),
Double(f64),
OptionalDouble(Option<f64>),
Bool(bool),
OptionalBool(Option<bool>),
Text(String),
OptionalText(Option<String>),
DateTime(DateTime<Utc>),
OptionalDateTime(Option<DateTime<Utc>>),
}
impl Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Value::Int(n) => write!(f, "{}", n),
Value::OptionalInt(Some(n)) => write!(f, "{}", n),
Value::OptionalInt(None) => write!(f, "NULL"),
Value::BigInt(n) => write!(f, "{}", n),
Value::OptionalBigInt(Some(n)) => write!(f, "{}", n),
Value::OptionalBigInt(None) => write!(f, "NULL"),
Value::Float(n) => write!(f, "{}", n),
Value::OptionalFloat(Some(n)) => write!(f, "{}", n),
Value::OptionalFloat(None) => write!(f, "NULL"),
Value::Double(n) => write!(f, "{}", n),
Value::OptionalDouble(Some(n)) => write!(f, "{}", n),
Value::OptionalDouble(None) => write!(f, "NULL"),
Value::Bool(n) => write!(f, "{}", n),
Value::OptionalBool(Some(n)) => write!(f, "{}", n),
Value::OptionalBool(None) => write!(f, "NULL"),
Value::Text(s) => write!(f, "'{}'", s.replace("'", "''")),
Value::OptionalText(Some(s)) => write!(f, "'{}'", s.replace("'", "''")),
Value::OptionalText(None) => write!(f, "NULL"),
Value::DateTime(n) => write!(f, "{}", n),
Value::OptionalDateTime(Some(n)) => write!(f, "{}", n),
Value::OptionalDateTime(None) => write!(f, "NULL"),
}
}
}