
Should make it more dsecriptive with really weird input and prevent even more confusing 500 responses to the client + db-lib::Channels::add now also potentially returns this new RestrictedInput variant
199 lines
7.8 KiB
Rust
199 lines
7.8 KiB
Rust
use mysql_async::{params, Pool, Conn};
|
|
use mysql_async::prelude::Queryable;
|
|
use mysql_async::error::Error as SqlError;
|
|
|
|
use async_trait::async_trait;
|
|
|
|
use crate::{VarChar, UBigInt, Integer};
|
|
use crate::common::FromDB;
|
|
use crate::{sql_err, no_conn, Response};
|
|
|
|
use serde::Serialize;
|
|
|
|
#[derive(Serialize)]
|
|
pub struct Channel {
|
|
pub id: UBigInt,
|
|
pub name: VarChar,
|
|
pub description: Option<VarChar>,
|
|
pub kind: Integer
|
|
}
|
|
|
|
pub const VOICE_CHANNEL: Integer = 1;
|
|
pub const TEXT_CHANNEL: Integer = 2;
|
|
|
|
#[async_trait]
|
|
impl FromDB<Channel, Integer> for Channel {
|
|
// id name desc kind
|
|
type Row = Option<(UBigInt, VarChar, Option<VarChar>, Integer)>;
|
|
|
|
async fn get(p: &Pool, id: UBigInt) -> Response<Channel> {
|
|
//! Retrieves a Full single Channel row from the DB or fails in a
|
|
//! fashion described by crate::Response<Channel>
|
|
//! @param p -> SqlPool
|
|
//! @param id -> UBigInt
|
|
//! @return on_success -> Response::Row(Channel)
|
|
//! @return on_fail -> Response::{Empty, Other<String>}
|
|
if let Ok(conn) = p.get_conn().await {
|
|
let q = "SELECT id, name, description, kind FROM channels WHERE id = :id";
|
|
let result: Result<(Conn, Self::Row), SqlError> =
|
|
conn.first_exec(q, params!{"id" => id}).await;
|
|
if let Ok((_, row)) = result {
|
|
return match row {
|
|
Some(row) => Response::Row(Channel {
|
|
id: id,
|
|
name: row.1,
|
|
description: row.2,
|
|
kind: row.3
|
|
|
|
}),
|
|
None => Response::Empty
|
|
}
|
|
}
|
|
return Response::Other(no_conn!("Invite::FromDB::get fetch failed"));
|
|
}
|
|
return Response::Other(no_conn!("Invite::FromDB::get"));
|
|
}
|
|
|
|
async fn update(p: &Pool, row: Self) -> Response<Channel> {
|
|
//! Updates a whole single based on a given Row Of Channel Type
|
|
//! @param p -> SqlPool
|
|
//! @param row -> Channel
|
|
//! @return on_success -> Response::Success
|
|
//! @return on_failure -> Response::Other
|
|
if let Ok(conn) = p.get_conn().await {
|
|
let q = "UPDATE channels
|
|
SET name = :name, description = :desc, kind = :kind
|
|
WHERE id = :id";
|
|
let result: Result<Conn, SqlError> =
|
|
conn.drop_exec(q, params!{
|
|
"id" => row.id,
|
|
"name" => row.name,
|
|
"desc" => row.description,
|
|
"kind" => row.kind
|
|
}).await;
|
|
return match result {
|
|
Ok(_) => Response::Success,
|
|
Err(_) => Response::Other(sql_err!("Invite::FromDB::update Update failed"))
|
|
}
|
|
}
|
|
return Response::Other(no_conn!("Invite::FromDB::get connection failed"));
|
|
}
|
|
|
|
async fn delete(p: &Pool, id: UBigInt) -> Response<Channel> {
|
|
//! Deletes channel given UBigInt as the row key
|
|
//! @param p -> SqlPool
|
|
//! @param id -> UBigInt
|
|
//! @return on_success -> Response::Success
|
|
//! @return on_failure -> Response::Other
|
|
if let Ok(conn) = p.get_conn().await {
|
|
let q = "DELETE FROM channels WHERE id = :id";
|
|
let result: Result<Conn, SqlError> =
|
|
conn.drop_exec(q, params!{"id" => id}).await;
|
|
return match result {
|
|
Ok(_) => Response::Success,
|
|
Err(sql) => Response::Other(sql_err!(sql))
|
|
}
|
|
}
|
|
else {
|
|
return Response::Other(no_conn!("Member::FromDB::delete"))
|
|
}
|
|
}
|
|
|
|
async fn filter(p: &Pool, kind: Integer) -> Response<Channel> {
|
|
//! @returns -> on success : Response::Set(Vec<Channel>)
|
|
//! @returns -> on empty set : Response::Set(EmptyVector)
|
|
//! @params -> on fail : Response::Other
|
|
|
|
// NOTE: used for mapping datasets to vectors
|
|
let map_rows = |row| {
|
|
let (id, name, desc, k): (UBigInt, VarChar, Option<VarChar>, Integer) =
|
|
mysql_async::from_row(row);
|
|
Channel {
|
|
id: id,
|
|
name: name,
|
|
description: desc,
|
|
kind: k
|
|
}
|
|
};
|
|
return match (p.get_conn().await, kind) {
|
|
// Filter for text/voice channels specifically
|
|
(Ok(conn), VOICE_CHANNEL..=TEXT_CHANNEL) => { // @NOTE: voice channel and text_channel are literally 1 & 2 respectively
|
|
let q = "SELECT id, name, description, kind FROM channels WHERE kind = :kind";
|
|
let q_result = conn.prep_exec(q, params!{"kind" => kind}).await;
|
|
if let Ok(res) = q_result {
|
|
let mapping_result = res.map_and_drop(map_rows).await;
|
|
return match mapping_result {
|
|
Ok((_, channels)) => Response::Set(channels),
|
|
Err(_) => Response::Other(sql_err!("db::Channels::filter @with params"))
|
|
};
|
|
}
|
|
else {
|
|
Response::Other(sql_err!(""))
|
|
}
|
|
},
|
|
/*
|
|
* Here we are attempting to get all the channels with no filters applied
|
|
* This should fetch everything basically in our channels registry
|
|
*/
|
|
(Ok(conn), _) => {
|
|
let q = "SELECT id, name, description, kind FROM channels";
|
|
if let Ok(query) = conn.prep_exec(q, ()).await {
|
|
let mapping_r = query.map_and_drop(map_rows).await;
|
|
|
|
return match mapping_r {
|
|
Ok((_, channels)) => Response::Set(channels),
|
|
Err(_) => Response::Other(sql_err!("db::Channels::filter @no params"))
|
|
};
|
|
}
|
|
else {
|
|
Response::Other(sql_err!("db::Channels::filter @no params @no initial query"))
|
|
}
|
|
},
|
|
(Err(_), _) => {Response::Other(no_conn!("Channel::FromDB::filter"))}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Channel {
|
|
pub async fn add(p: &Pool, name: &str, desc: &str, kind: Integer) -> Response<Self> {
|
|
//! @returns on success -> Response::Row<Channel>
|
|
//! @returns on partial success -> Response::Empty
|
|
//! @returns on failure -> Response::Other
|
|
if let Ok(conn) = p.get_conn().await {
|
|
let q = "INSERT INTO channels (name, description, kind) VALUES (:n, :d, :k)";
|
|
let insert_result = conn.drop_exec(q, params!{
|
|
"n" => name,
|
|
"d" => desc,
|
|
"k" => kind
|
|
}).await;
|
|
if let Ok(conn) = insert_result {
|
|
// This is only kosher because names are enforced as unique by sql
|
|
let q = "SELECT id FROM channels WHERE name = :name";
|
|
let fetch_result : Result<(Conn, Option<UBigInt>), SqlError> =
|
|
conn.first_exec(q, params!{"name" => name}).await;
|
|
|
|
return match fetch_result {
|
|
Ok((_, id_opt)) => {
|
|
if let Some(id) = id_opt {
|
|
Response::Row(Channel {
|
|
id: id,
|
|
name: name.into(),
|
|
description: Some(desc.into()),
|
|
kind: kind
|
|
})
|
|
}
|
|
else { Response::Empty }
|
|
},
|
|
Err(_) => Response::Empty
|
|
};
|
|
}
|
|
else {
|
|
return Response::RestrictedInput(
|
|
"Could not add channel, make sure the name is unique and not longer than 256 bytes".into());
|
|
}
|
|
}
|
|
return Response::Other(no_conn!("db::channels::add"))
|
|
}
|
|
}
|
|
|