diff --git a/Cargo.lock b/Cargo.lock index 005a77e..9a658cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -187,6 +187,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -346,6 +355,18 @@ dependencies = [ "tokio-postgres", ] +[[package]] +name = "diesel-derive-enum" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81c5131a2895ef64741dad1d483f358c2a229a3a2d1b256778cdc5e146db64d4" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "diesel_derives" version = "2.2.4" @@ -413,7 +434,7 @@ checksum = "139ae9aca7527f85f26dd76483eb38533fd84bd571065da1739656ef71c5ff5b" dependencies = [ "darling", "either", - "heck", + "heck 0.5.0", "proc-macro2", "quote", "syn", @@ -428,6 +449,18 @@ dependencies = [ "serde", ] +[[package]] +name = "enum_stringify" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9ff9e4dbfa8fb0fd68ff9e4da874ad10774af372f0e2b8d1649c63026434d37" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "envconfig" version = "0.11.0" @@ -700,7 +733,9 @@ version = "0.1.0" dependencies = [ "diesel", "diesel-async", + "diesel-derive-enum", "dotenvy", + "enum_stringify", "envconfig", "serde", "teloxide", @@ -733,6 +768,12 @@ dependencies = [ "hashbrown 0.15.2", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "heck" version = "0.5.0" @@ -1918,7 +1959,7 @@ checksum = "4e9f90acc5ab146a99bf5061a7eb4976b573f560bc898ef3bf8435448dd5e7ad" dependencies = [ "dotenvy", "either", - "heck", + "heck 0.5.0", "hex", "once_cell", "proc-macro2", @@ -2131,7 +2172,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3118a980ed2ec11f73d9495a6606905bd74726e3ffe95a42fbeb187ded8fdbf4" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", "syn", @@ -2419,6 +2460,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "unicode-xid" version = "0.2.6" diff --git a/Cargo.toml b/Cargo.toml index 3543e31..23a9fa1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,9 @@ edition = "2021" [dependencies] diesel = { version = "2.2.8", features = ["postgres"] } diesel-async = { version = "0.5.2", features = ["bb8", "postgres"] } +diesel-derive-enum = "2.1.0" dotenvy = "0.15.7" +enum_stringify = "0.6.3" envconfig = "0.11.0" serde = { version = "1.0.219", features = ["derive", "serde_derive"] } teloxide = { version = "0.14.0", features = ["macros", "postgres-storage-nativetls"] } diff --git a/diesel.toml b/diesel.toml index 0010335..b835004 100644 --- a/diesel.toml +++ b/diesel.toml @@ -3,7 +3,7 @@ [print_schema] file = "src/db/schema.rs" -custom_type_derives = ["diesel::query_builder::QueryId", "Clone"] +custom_type_derives = ["diesel_derive_enum::DbEnum", "diesel::query_builder::QueryId", "Clone"] [migrations_directory] dir = "./migrations" diff --git a/migrations/2025-04-07-124248_extend_user_table/down.sql b/migrations/2025-04-07-124248_extend_user_table/down.sql new file mode 100644 index 0000000..10636f0 --- /dev/null +++ b/migrations/2025-04-07-124248_extend_user_table/down.sql @@ -0,0 +1,5 @@ +ALTER TABLE users +DROP COLUMN first_name, +DROP COLUMN last_name, +DROP COLUMN username, +DROP COLUMN language_code; diff --git a/migrations/2025-04-07-124248_extend_user_table/up.sql b/migrations/2025-04-07-124248_extend_user_table/up.sql new file mode 100644 index 0000000..b2d3353 --- /dev/null +++ b/migrations/2025-04-07-124248_extend_user_table/up.sql @@ -0,0 +1,5 @@ +ALTER TABLE users +ADD COLUMN first_name VARCHAR(255), +ADD COLUMN last_name VARCHAR(255), +ADD COLUMN username VARCHAR(255), +ADD COLUMN language_code VARCHAR(10); diff --git a/migrations/2025-04-07-125633_non_optional_firstname/down.sql b/migrations/2025-04-07-125633_non_optional_firstname/down.sql new file mode 100644 index 0000000..38e94eb --- /dev/null +++ b/migrations/2025-04-07-125633_non_optional_firstname/down.sql @@ -0,0 +1,6 @@ +-- This file should undo anything in `up.sql` + + + +ALTER TABLE "users" ALTER COLUMN "first_name" DROP NOT NULL; + diff --git a/migrations/2025-04-07-125633_non_optional_firstname/up.sql b/migrations/2025-04-07-125633_non_optional_firstname/up.sql new file mode 100644 index 0000000..76ebd78 --- /dev/null +++ b/migrations/2025-04-07-125633_non_optional_firstname/up.sql @@ -0,0 +1,6 @@ +-- Your SQL goes here + + + +ALTER TABLE "users" ALTER COLUMN "first_name" SET NOT NULL; + diff --git a/migrations/2025-04-07-130716_event_table/down.sql b/migrations/2025-04-07-130716_event_table/down.sql new file mode 100644 index 0000000..92c3065 --- /dev/null +++ b/migrations/2025-04-07-130716_event_table/down.sql @@ -0,0 +1,2 @@ +DROP TABLE events; + diff --git a/migrations/2025-04-07-130716_event_table/up.sql b/migrations/2025-04-07-130716_event_table/up.sql new file mode 100644 index 0000000..58a105b --- /dev/null +++ b/migrations/2025-04-07-130716_event_table/up.sql @@ -0,0 +1,4 @@ +CREATE TABLE events ( + id SERIAL PRIMARY KEY, + time TIMESTAMP UNIQUE NOT NULL +); diff --git a/migrations/2025-04-07-130908_reservation_table/down.sql b/migrations/2025-04-07-130908_reservation_table/down.sql new file mode 100644 index 0000000..aa2a4be --- /dev/null +++ b/migrations/2025-04-07-130908_reservation_table/down.sql @@ -0,0 +1,2 @@ +DROP TABLE reservations; +DROP TYPE reservation_status; diff --git a/migrations/2025-04-07-130908_reservation_table/up.sql b/migrations/2025-04-07-130908_reservation_table/up.sql new file mode 100644 index 0000000..2850d65 --- /dev/null +++ b/migrations/2025-04-07-130908_reservation_table/up.sql @@ -0,0 +1,10 @@ +CREATE TYPE reservation_status AS ENUM ('booked', 'paid'); + +CREATE TABLE reservations ( + id SERIAL PRIMARY KEY, + user_id INTEGER REFERENCES users(id), + entered_name VARCHAR(255), + booked_time TIMESTAMP NOT NULL, + event_id INTEGER REFERENCES events(id), + status reservation_status NOT NULL +); diff --git a/migrations/2025-04-07-135609_stringify_reservation_status/down.sql b/migrations/2025-04-07-135609_stringify_reservation_status/down.sql new file mode 100644 index 0000000..305d59a --- /dev/null +++ b/migrations/2025-04-07-135609_stringify_reservation_status/down.sql @@ -0,0 +1,2 @@ +CREATE TYPE reservation_status AS ENUM ('booked', 'paid'); +ALTER TABLE reservations ALTER COLUMN status TYPE reservation_status USING status::reservation_status; diff --git a/migrations/2025-04-07-135609_stringify_reservation_status/up.sql b/migrations/2025-04-07-135609_stringify_reservation_status/up.sql new file mode 100644 index 0000000..ce0762b --- /dev/null +++ b/migrations/2025-04-07-135609_stringify_reservation_status/up.sql @@ -0,0 +1,2 @@ +ALTER TABLE reservations ALTER COLUMN status TYPE VARCHAR; +DROP TYPE reservation_status; diff --git a/src/db.rs b/src/db.rs index c908ca0..85917c8 100644 --- a/src/db.rs +++ b/src/db.rs @@ -9,6 +9,24 @@ use diesel_async::pooled_connection::bb8::Pool; use diesel_async::pooled_connection::AsyncDieselConnectionManager; use diesel_async::AsyncPgConnection; use diesel_async::RunQueryDsl; +use enum_stringify::EnumStringify; + +#[derive(EnumStringify)] +#[enum_stringify(case = "flat")] +pub enum ReservationStatus { + Booked, + Paid, +} + +pub trait GetReservationStatus { + fn get_status(&self) -> Option; +} + +impl GetReservationStatus for models::Reservation { + fn get_status(&self) -> Option { + ReservationStatus::try_from(self.status.clone()).ok() + } +} #[derive(Clone)] pub struct DB { diff --git a/src/db/models.rs b/src/db/models.rs index 6a0ba1b..af1f7a2 100644 --- a/src/db/models.rs +++ b/src/db/models.rs @@ -3,8 +3,17 @@ #![allow(unused)] #![allow(clippy::all)] + +use chrono::NaiveDateTime; use diesel::prelude::*; -#[derive(Queryable, Debug)] +#[derive(Queryable, Debug, Identifiable)] +#[diesel(table_name = events)] +pub struct Event { + pub id: i32, + pub time: NaiveDateTime, +} + +#[derive(Queryable, Debug, Identifiable)] #[diesel(table_name = literals)] pub struct Literal { pub id: i32, @@ -12,7 +21,7 @@ pub struct Literal { pub value: String, } -#[derive(Queryable, Debug)] +#[derive(Queryable, Debug, Identifiable)] #[diesel(table_name = messages)] pub struct Message { pub id: i32, @@ -21,9 +30,33 @@ pub struct Message { pub token: String, } +#[derive(Queryable, Debug, Identifiable)] +#[diesel(table_name = reservations)] +pub struct Reservation { + pub id: i32, + pub user_id: Option, + pub entered_name: Option, + pub booked_time: NaiveDateTime, + pub event_id: Option, + pub status: String, +} + +#[derive(Queryable, Debug, Identifiable)] +#[diesel(primary_key(chat_id))] +#[diesel(table_name = teloxide_dialogues)] +pub struct TeloxideDialogue { + pub chat_id: i64, + pub dialogue: Vec, +} + #[derive(Queryable, Debug)] #[diesel(table_name = users)] pub struct User { pub id: i64, pub is_admin: bool, + pub first_name: String, + pub last_name: Option, + pub username: Option, + pub language_code: Option, } + diff --git a/src/db/schema.rs b/src/db/schema.rs index 78542e1..62c1f97 100644 --- a/src/db/schema.rs +++ b/src/db/schema.rs @@ -1,5 +1,12 @@ // @generated automatically by Diesel CLI. +diesel::table! { + events (id) { + id -> Int4, + time -> Timestamp, + } +} + diesel::table! { literals (id) { id -> Int4, @@ -20,10 +27,47 @@ diesel::table! { } diesel::table! { - users (id) { - id -> Int8, - is_admin -> Bool, + reservations (id) { + id -> Int4, + user_id -> Nullable, + #[max_length = 255] + entered_name -> Nullable, + booked_time -> Timestamp, + event_id -> Nullable, + status -> Varchar, } } -diesel::allow_tables_to_appear_in_same_query!(literals, messages, users,); +diesel::table! { + teloxide_dialogues (chat_id) { + chat_id -> Int8, + dialogue -> Bytea, + } +} + +diesel::table! { + users (id) { + id -> Int8, + is_admin -> Bool, + #[max_length = 255] + first_name -> Varchar, + #[max_length = 255] + last_name -> Nullable, + #[max_length = 255] + username -> Nullable, + #[max_length = 10] + language_code -> Nullable, + } +} + +diesel::joinable!(reservations -> events (event_id)); +diesel::joinable!(reservations -> users (user_id)); + +diesel::allow_tables_to_appear_in_same_query!( + events, + literals, + messages, + reservations, + teloxide_dialogues, + users, +);