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, pub kind: Integer } pub const VOICE_CHANNEL: Integer = 1; pub const TEXT_CHANNEL: Integer = 2; #[async_trait] impl FromDB for Channel { // id name desc kind type Row = Option<(UBigInt, VarChar, Option, Integer)>; async fn get(p: &Pool, id: UBigInt) -> Response { //! Retrieves a Full single Channel row from the DB or fails in a //! fashion described by crate::Response //! @param p -> SqlPool //! @param id -> UBigInt //! @return on_success -> Response::Row(Channel) //! @return on_fail -> Response::{Empty, Other} 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 { //! 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.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 { //! 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.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 { //! @returns -> on success : Response::Set(Vec) //! @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, 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 { //! @returns on success -> Response::Row //! @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), 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 }; } // TODO: change this to return Result<> which should help with ambiguity return Response::Other("Could not fetch new channel".into()); } return Response::Other(no_conn!("db::channels::add")) } }