#![allow(unused)] use log::{info, warn}; use sqlx::{migrate::MigrateDatabase, PgPool, Postgres, Sqlite, SqlitePool}; pub mod models; mod postgres; mod sqlite; /// Utility for interacting with the database #[derive(Clone)] pub enum DataBase { /// Used for Sqlite database Sqlite(sqlx::Pool), /// Used for Postgres database Postgres(sqlx::Pool), } /// Error type for handling DB related errors pub enum DbError { /// No such entry found NotFound, /// Database related error SqlxError(sqlx::Error), } impl From for DbError { fn from(value: sqlx::Error) -> Self { match value { sqlx::Error::RowNotFound => DbError::NotFound, _ => DbError::SqlxError(value), } } } type DbResult = Result; impl DataBase { /// Database backed by SQLite pub async fn sqlite() -> Self { // Check if db exists, if not create it. if !sqlx::Sqlite::database_exists("sqlite:gunnhildr.db") .await .expect("failed to connect to db") { warn!("No SQLite database found, if this is the first time you are starting Gunnhildr, you can safely ignore this."); sqlx::Sqlite::create_database("sqlite:gunnhildr.db") .await .expect("failed to create SQLite Database"); info!("Created new SQLite Database"); } let pool = SqlitePool::connect("sqlite:gunnhildr.db").await.unwrap(); // run migrations sqlx::migrate!("migrations/sqlite") .run(&pool) .await .expect("Failed to apply migration!"); info!("Applied migrations."); Self::Sqlite(pool) } /// Database backed by Postgres pub async fn postgres(url: &str) -> Self { // check if database exists and create one if not if !sqlx::Postgres::database_exists(url) .await .expect("failed to connect to db") { warn!("No Postgres database found, if this is the first time you are starting Gunnhildr, you can safely ignore this."); sqlx::Postgres::create_database(url) .await .expect("failed to create Postgres Database!"); info!("Created new Postgres Database"); } let pool = PgPool::connect("url").await.unwrap(); // run migrations sqlx::migrate!("migrations/postgres") .run(&pool) .await .expect("Failed to apply migration!"); Self::Postgres(pool) } /// Tries to fetch a book from the database pub async fn get_book(&self, id: u32) -> DbResult { match self { DataBase::Sqlite(pool) => sqlite::sqlite_book(pool, id).await, DataBase::Postgres(pool) => todo!(), } } /// Tries to create a book and returns the book's id if successful pub async fn create_book( &self, title: String, description: String, author_id: u32, ) -> DbResult { match self { DataBase::Sqlite(pool) => { sqlite::sqlite_book_create(pool, title, description, author_id).await } DataBase::Postgres(pool) => todo!(), } } /// Tries to fetch a chapter from the database pub async fn get_chapter(&self, id: u32) -> DbResult { match self { DataBase::Sqlite(pool) => sqlite::sqlite_chapter(pool, id).await, DataBase::Postgres(pool) => todo!(), } } /// Tries to create a chapter and returns the chapter's id if successfu pub async fn create_chapter( &self, title: String, text: String, book_id: u32, author_id: u32, ) -> DbResult { match self { DataBase::Sqlite(pool) => { sqlite::sqlite_chapter_create(pool, title, text, book_id, author_id).await } DataBase::Postgres(pool) => todo!(), } } /// Tries to fetch a user from the database pub async fn get_user(&self, id: u32) -> DbResult { match self { DataBase::Sqlite(pool) => sqlite::sqlite_user(pool, id).await, DataBase::Postgres(pool) => todo!(), } } /// Tries to create a user and returns the user's id if successful pub async fn create_user(&self, name: String) -> DbResult { match self { DataBase::Sqlite(pool) => sqlite::sqlite_user_create(pool, name).await, DataBase::Postgres(pool) => todo!(), } } }