BotTerre/libs/database/src/trivial.rs

219 lines
6.2 KiB
Rust

use chrono::{DateTime, Utc};
use clickhouse::Row;
use clickhouse_pool::traits::Model;
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use uuid::Uuid;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum TrivialRewardKind {
OnlyTheFirstOne = 0,
TopThree = 1,
TopFive = 2,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum TrivialStatus {
Init = 0,
Started = 1,
Finished = 2,
Paused = 3,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Row, Serialize, Deserialize)]
pub struct Trivial {
#[serde(with = "clickhouse::serde::uuid")]
pub id: Uuid,
pub name: String,
pub description: String,
pub guild_id: u64,
pub channel_id: u64,
#[serde(with = "clickhouse::serde::chrono::datetime64::millis")]
pub created_at: DateTime<Utc>,
#[serde(with = "clickhouse::serde::chrono::datetime64::millis")]
pub updated_at: DateTime<Utc>,
pub creator_id: u64,
pub updater_id: u64,
pub random_question: bool,
pub role_ping: u64,
pub role_ping_enabled: bool,
pub reward_kind: TrivialRewardKind,
pub reward_amount: u64,
/// Whether or not the bot should send an ephemeral message to the user when their answer is taken into account.
pub taken_into_account: bool,
pub status: TrivialStatus,
}
impl Default for Trivial {
fn default() -> Self {
Self {
id: Uuid::new_v4(),
name: String::new(),
description: String::new(),
guild_id: 0,
channel_id: 0,
created_at: Utc::now(),
updated_at: Utc::now(),
creator_id: 0,
updater_id: 0,
random_question: true,
role_ping: 0,
role_ping_enabled: false,
reward_kind: TrivialRewardKind::TopThree,
reward_amount: 3,
taken_into_account: true,
status: TrivialStatus::Init,
}
}
}
impl Trivial {
pub fn new(
name: String,
description: String,
guild_id: u64,
channel_id: u64,
creator_id: u64,
updater_id: u64,
) -> Self {
Self {
name,
description,
guild_id,
channel_id,
creator_id,
updater_id,
..Default::default()
}
}
}
impl Model for Trivial {
type T = Trivial;
fn table_name() -> &'static str {
"trivial"
}
fn create_table_sql() -> &'static str {
r#"
CREATE TABLE IF NOT EXISTS trivial (
id UUID PRIMARY KEY,
name String,
description String,
guild_id UInt64,
channel_id UInt64,
created_at DateTime64(3, 'UTC'),
updated_at DateTime64(3, 'UTC'),
creator_id UInt64,
updater_id UInt64,
random_question Bool,
role_ping UInt64,
role_ping_enabled Bool,
reward_kind Enum8('OnlyTheFirstOne' = 0, 'TopThree' = 1, 'TopFive' = 2),
reward_amount UInt64,
taken_into_account Bool,
status Enum8('Init' = 0, 'Started' = 1, 'Finished' = 2, 'Paused' = 3)
) ENGINE = MergeTree()
ORDER BY id
"#
}
fn column_names() -> Vec<&'static str> {
vec![
"id",
"name",
"description",
"guild_id",
"channel_id",
"created_at",
"updated_at",
"creator_id",
"updater_id",
"random_question",
"role_ping",
"role_ping_enabled",
"reward_kind",
"reward_amount",
"taken_into_account",
"status",
]
}
fn to_row(&self) -> (Vec<&'static str>, Vec<String>) {
(
Self::column_names(),
vec![
format!("'{}'", self.id),
format!("'{}'", self.name),
format!("'{}'", self.description),
self.guild_id.to_string(),
self.channel_id.to_string(),
format!("'{}'", self.created_at.to_rfc3339()),
format!("'{}'", self.updated_at.to_rfc3339()),
self.creator_id.to_string(),
self.updater_id.to_string(),
self.random_question.to_string(),
self.role_ping.to_string(),
self.role_ping_enabled.to_string(),
format!("'{:?}'", serde_json::to_string(&self.reward_kind)),
self.reward_amount.to_string(),
self.taken_into_account.to_string(),
format!("'{:?}'", serde_json::to_string(&self.status)),
],
)
}
fn insert_query(&self) -> String {
let (columns, values) = self.to_row();
let columns_str = columns.join(", ");
let values_str = values.join(", ");
format!(
"INSERT INTO {} ({}) VALUES ({})",
Self::table_name(),
columns_str,
values_str
)
}
fn batch_insert_query(items: &[Self::T]) -> String {
let mut queries = Vec::new();
for item in items {
let (columns, values) = item.to_row();
let columns_str = columns.join(", ");
let values_str = values.join(", ");
queries.push(format!(
"INSERT INTO {} ({}) VALUES ({})",
Self::table_name(),
columns_str,
values_str
));
}
queries.join("; ")
}
fn build_select_query(
where_clause: Option<&str>,
limit: Option<u64>,
offset: Option<u64>,
) -> String {
let mut query = format!("SELECT * FROM {}", Self::table_name());
if let Some(where_clause) = where_clause {
query.push_str(&format!(" WHERE {}", where_clause));
}
if let Some(limit) = limit {
query.push_str(&format!(" LIMIT {}", limit));
}
if let Some(offset) = offset {
query.push_str(&format!(" OFFSET {}", offset));
}
query
}
}