From 20af44c357f47d9d8894101f9e695a38430cc3a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20Kaan=20G=C3=9CM=C3=9C=C5=9E?= <96421894+Tahinli@users.noreply.github.com> Date: Tue, 3 Dec 2024 21:33:42 +0300 Subject: [PATCH] feat: :sparkles: sqlx --- .env | 1 + Cargo.toml | 16 +++-- configs/database_config.toml | 3 +- entity/Cargo.toml | 7 -- entity/src/lib.rs | 1 - migration/Cargo.toml | 22 ------ migration/README.md | 41 ----------- migration/src/lib.rs | 12 ---- .../src/m20241202_201137_create_user_table.rs | 69 ------------------ migration/src/main.rs | 6 -- .../20241203135558_create_user_table.down.sql | 3 + .../20241203135558_create_user_table.up.sql | 12 ++++ src/database.rs | 28 ++++++-- src/database/user.rs | 72 +++++++++++++++++++ src/feature/user.rs | 30 ++------ src/lib.rs | 6 +- 16 files changed, 135 insertions(+), 194 deletions(-) create mode 100644 .env delete mode 100644 entity/Cargo.toml delete mode 100644 entity/src/lib.rs delete mode 100644 migration/Cargo.toml delete mode 100644 migration/README.md delete mode 100644 migration/src/lib.rs delete mode 100644 migration/src/m20241202_201137_create_user_table.rs delete mode 100644 migration/src/main.rs create mode 100644 migrations/20241203135558_create_user_table.down.sql create mode 100644 migrations/20241203135558_create_user_table.up.sql diff --git a/.env b/.env new file mode 100644 index 0000000..7eca3fb --- /dev/null +++ b/.env @@ -0,0 +1 @@ +DATABASE_URL=postgres://root:root@localhost:5432/rust_forum \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 11d288c..79d74d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,16 +3,22 @@ name = "rust_forum" version = "0.1.0" edition = "2021" -[workspace] -members = [".", "entity", "migration"] +[lints.rust] +unsafe_code = "forbid" + +[profile.release] +opt-level = 3 +lto = true +overflow-checks = true +codegen-units = 1 +panic = "abort" +strip = "symbols" [dependencies] -entity = { path = "entity" } -migration = { path = "migration" } axum = "0.7.9" chrono = { version = "0.4.38", features = ["serde"] } -sea-orm = { version = "1.1.2", features = ["macros", "runtime-tokio-rustls", "sqlx-postgres", "with-chrono", "with-json"] } serde = { version = "1.0.215", features = ["derive"] } serde_json = "1.0.133" +sqlx = { version = "0.8.2", features = ["chrono", "macros", "postgres", "runtime-tokio-rustls"] } tokio = { version = "1.41.1", features = ["full"] } tower-http = { version = "0.6.2", features = ["cors"] } diff --git a/configs/database_config.toml b/configs/database_config.toml index 3f790de..88aab43 100644 --- a/configs/database_config.toml +++ b/configs/database_config.toml @@ -3,4 +3,5 @@ address = "localhost:5432" username = "root" password = "root" database = "rust_forum" -backend = "postgres" \ No newline at end of file +backend = "postgres" +connection_pool_size = "100" \ No newline at end of file diff --git a/entity/Cargo.toml b/entity/Cargo.toml deleted file mode 100644 index f72ff70..0000000 --- a/entity/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "entity" -version = "0.1.0" -edition = "2021" - -[dependencies] -sea-orm = "1.1.2" \ No newline at end of file diff --git a/entity/src/lib.rs b/entity/src/lib.rs deleted file mode 100644 index 8b13789..0000000 --- a/entity/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/migration/Cargo.toml b/migration/Cargo.toml deleted file mode 100644 index c1dd07b..0000000 --- a/migration/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "migration" -version = "0.1.0" -edition = "2021" -publish = false - -[lib] -name = "migration" -path = "src/lib.rs" - -[dependencies] -async-std = { version = "1", features = ["attributes", "tokio1"] } - -[dependencies.sea-orm-migration] -version = "1.1.0" -features = [ - # Enable at least one `ASYNC_RUNTIME` and `DATABASE_DRIVER` feature if you want to run migration via CLI. - # View the list of supported features at https://www.sea-ql.org/SeaORM/docs/install-and-config/database-and-async-runtime. - # e.g. - "runtime-tokio-rustls", # `ASYNC_RUNTIME` feature - "sqlx-postgres", # `DATABASE_DRIVER` feature -] diff --git a/migration/README.md b/migration/README.md deleted file mode 100644 index 3b438d8..0000000 --- a/migration/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# Running Migrator CLI - -- Generate a new migration file - ```sh - cargo run -- generate MIGRATION_NAME - ``` -- Apply all pending migrations - ```sh - cargo run - ``` - ```sh - cargo run -- up - ``` -- Apply first 10 pending migrations - ```sh - cargo run -- up -n 10 - ``` -- Rollback last applied migrations - ```sh - cargo run -- down - ``` -- Rollback last 10 applied migrations - ```sh - cargo run -- down -n 10 - ``` -- Drop all tables from the database, then reapply all migrations - ```sh - cargo run -- fresh - ``` -- Rollback all applied migrations, then reapply all migrations - ```sh - cargo run -- refresh - ``` -- Rollback all applied migrations - ```sh - cargo run -- reset - ``` -- Check the status of all migrations - ```sh - cargo run -- status - ``` diff --git a/migration/src/lib.rs b/migration/src/lib.rs deleted file mode 100644 index aa3add6..0000000 --- a/migration/src/lib.rs +++ /dev/null @@ -1,12 +0,0 @@ -pub use sea_orm_migration::prelude::*; - -mod m20241202_201137_create_user_table; - -pub struct Migrator; - -#[async_trait::async_trait] -impl MigratorTrait for Migrator { - fn migrations() -> Vec> { - vec![Box::new(m20241202_201137_create_user_table::Migration)] - } -} diff --git a/migration/src/m20241202_201137_create_user_table.rs b/migration/src/m20241202_201137_create_user_table.rs deleted file mode 100644 index fdb18a6..0000000 --- a/migration/src/m20241202_201137_create_user_table.rs +++ /dev/null @@ -1,69 +0,0 @@ -use extension::postgres::Type; -use sea_orm::{EnumIter, Iterable}; -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(DeriveIden)] -struct Role; - -#[derive(Iden, EnumIter)] -enum RoleType { - #[iden = "Default"] - Default, - #[iden = "Admin"] - Admin, -} - -#[derive(DeriveIden)] -enum User { - Table, - Id, - Name, - Surname, - Gender, - BirthDate, - Email, - Role, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_type( - Type::create() - .as_enum(Role) - .values(RoleType::iter()) - .to_owned(), - ) - .await?; - manager - .create_table( - Table::create() - .table(User::Table) - .if_not_exists() - .col(pk_auto(User::Id)) - .col(string(User::Name)) - .col(string(User::Surname)) - .col(string(User::Email)) - .col(date_time(User::BirthDate)) - .col(boolean(User::Gender)) - .col(enumeration( - User::Role, - Alias::new("role"), - RoleType::iter(), - )) - .to_owned(), - ) - .await?; - Ok(()) - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(User::Table).to_owned()) - .await - } -} diff --git a/migration/src/main.rs b/migration/src/main.rs deleted file mode 100644 index c6b6e48..0000000 --- a/migration/src/main.rs +++ /dev/null @@ -1,6 +0,0 @@ -use sea_orm_migration::prelude::*; - -#[async_std::main] -async fn main() { - cli::run_cli(migration::Migrator).await; -} diff --git a/migrations/20241203135558_create_user_table.down.sql b/migrations/20241203135558_create_user_table.down.sql new file mode 100644 index 0000000..7d2e7e3 --- /dev/null +++ b/migrations/20241203135558_create_user_table.down.sql @@ -0,0 +1,3 @@ +-- Add down migration script here +DROP TABLE IF EXISTS "user"; +DROP TYPE IF EXISTS role; \ No newline at end of file diff --git a/migrations/20241203135558_create_user_table.up.sql b/migrations/20241203135558_create_user_table.up.sql new file mode 100644 index 0000000..571890a --- /dev/null +++ b/migrations/20241203135558_create_user_table.up.sql @@ -0,0 +1,12 @@ +-- Add up migration script here +DROP TYPE IF EXISTS role; +CREATE TYPE role AS ENUM('Zero', 'Hero'); +CREATE TABLE IF NOT EXISTS "user"( + id BIGSERIAL PRIMARY KEY NOT NULL, + name VARCHAR(255) NOT NULL, + surname VARCHAR(255) NOT NULL, + gender boolean NOT NULL, + birth_date DATE NOT NULL, + email VARCHAR(255) NOT NULL UNIQUE, + role ROLE NOT NULL DEFAULT 'Zero' +); \ No newline at end of file diff --git a/src/database.rs b/src/database.rs index 9ead1ce..8f19e64 100644 --- a/src/database.rs +++ b/src/database.rs @@ -5,12 +5,15 @@ pub mod user; use std::time::Duration; -use sea_orm::{Database, DatabaseConnection}; +use sqlx::{postgres::PgPoolOptions, Connection, Pool, Postgres}; use tokio::time::sleep; use crate::DatabaseConfig; -pub async fn establish_connection() -> DatabaseConnection { +pub async fn set_database_up(database_connection: &Pool) { + sqlx::migrate!().run(database_connection).await.unwrap(); +} +pub async fn establish_connection() -> Pool { let database_config = DatabaseConfig::default(); let connection_string = format!( "{}://{}:{}@{}/{}", @@ -20,12 +23,27 @@ pub async fn establish_connection() -> DatabaseConnection { database_config.address, database_config.database ); - Database::connect(connection_string).await.unwrap() + PgPoolOptions::new() + .max_connections(database_config.connection_pool_size) + .test_before_acquire(false) + .connect(&connection_string) + .await + .unwrap() } -pub async fn is_alive() -> bool { +pub async fn is_alive(database_connection: &Pool) -> bool { tokio::select! { - + database_connection = database_connection.acquire() => { + match database_connection { + Ok(mut database_connection) => { + match database_connection.ping().await { + Ok(_) => true, + Err(_) => false, + } + }, + Err(_) => false, + } + } _ = sleep(Duration::from_secs(1)) => false, } } diff --git a/src/database/user.rs b/src/database/user.rs index 8b13789..4ef09fc 100644 --- a/src/database/user.rs +++ b/src/database/user.rs @@ -1 +1,73 @@ +use chrono::NaiveDate; +use sqlx::{Pool, Postgres}; +use crate::feature::user::{Role, User}; + +pub async fn create_user( + name: &String, + surname: &String, + gender: bool, + birth_date: &NaiveDate, + email: &String, + database_connection: &Pool, +) -> Result { + sqlx::query_as!( + User, + r#" + INSERT INTO "user"(name, surname, gender, birth_date, email) + VALUES ($1, $2, $3, $4, $5) + RETURNING id, name, surname, gender, birth_date, email, role AS "role:Role" + "#, + name, + surname, + gender, + birth_date, + email + ) + .fetch_one(database_connection) + .await +} + +pub async fn read_user( + email: &String, + database_connection: &Pool, +) -> Result { + sqlx::query_as!(User, + r#" + SELECT id, name, surname, gender, birth_date, email, role AS "role:Role" FROM "user" WHERE "email" = $1 + "#, + email + ).fetch_one(database_connection).await +} + +pub async fn update_user( + id: i64, + name: &String, + surname: &String, + gender: &bool, + birth_date: &NaiveDate, + email: &String, + database_connection: &Pool, +) -> Result { + sqlx::query_as!(User, + r#" + UPDATE "user" SET "name" = $1, "surname" = $2, "gender" = $3, "birth_date" = $4, "email" = $5 WHERE "id" = $6 + RETURNING id, name, surname, gender, birth_date, email, role AS "role:Role" + "#, name, surname, gender, birth_date, email, id).fetch_one(database_connection).await +} + +pub async fn delete_user( + id: i64, + database_connection: &Pool, +) -> Result { + sqlx::query_as!( + User, + r#" + DELETE FROM "user" where id = $1 + RETURNING id, name, surname, gender, birth_date, email, role AS "role:Role" + "#, + id + ) + .fetch_one(database_connection) + .await +} diff --git a/src/feature/user.rs b/src/feature/user.rs index 7fc10db..1dd9c06 100644 --- a/src/feature/user.rs +++ b/src/feature/user.rs @@ -8,36 +8,20 @@ pub struct Contact { pub website: Option, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, sqlx::Type)] +#[sqlx(type_name = "role")] pub enum Role { - User, + Zero, + Hero, } #[derive(Debug, Serialize, Deserialize)] pub struct User { - pub name: Vec, - pub surname: Vec, + pub id: i64, + pub name: String, + pub surname: String, pub gender: bool, pub birth_date: NaiveDate, pub email: String, pub role: Role, } - -impl User { - pub async fn new( - name: Vec, - surname: Vec, - gender: bool, - birth_date: NaiveDate, - email: String, - ) -> Self { - Self { - name, - surname, - gender, - birth_date, - email, - role: Role::User, - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 2e6fe17..2381027 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ pub mod database; pub mod feature; pub mod utils; -use sea_orm::DatabaseConnection; +use sqlx::{Pool, Postgres}; use utils::naive_toml_parser; const DATABASE_CONFIG_FILE_LOCATION: &str = "./configs/database_config.toml"; @@ -15,6 +15,7 @@ pub struct DatabaseConfig { pub password: String, pub database: String, pub backend: String, + pub connection_pool_size: u32, } impl Default for DatabaseConfig { fn default() -> Self { @@ -22,6 +23,7 @@ impl Default for DatabaseConfig { if header == "[database_config]" { Self { + connection_pool_size: database_configs.pop().unwrap().parse().unwrap(), backend: database_configs.pop().unwrap(), database: database_configs.pop().unwrap(), password: database_configs.pop().unwrap(), @@ -55,5 +57,5 @@ impl Default for ServerConfig { #[derive(Debug, Clone)] pub struct AppState { - pub database_connection: DatabaseConnection, + pub database_connection: Pool, }