use mysql_async::{params, Pool, Conn}; use mysql_async::prelude::Queryable; use mysql_async::error::Error as SqlError; use async_trait::async_trait; use serde::Serialize; use crate::{Response, no_conn, sql_err}; use crate::{UBigInt, BigInt, Integer, VarChar}; use crate::common::{FromDB}; #[derive(Debug, Serialize)] pub struct Member { pub id: UBigInt, pub secret: VarChar, pub name: VarChar, pub joindate: BigInt, pub status: Integer, pub permissions: UBigInt, } pub const STATUS_ONLINE: Integer = 0; pub const STATUS_OFFLINE: Integer = 1; pub const STATUS_AWAY: Integer = 2; pub const STATUS_DO_NOT_DISTURB: Integer = 3; /* * * conn = getconn * result = conn.query * return response based on result * */ #[async_trait] impl FromDB for Member { type Row = Option<(VarChar, VarChar, BigInt, Integer, UBigInt)>; async fn get(p: &Pool, id: UBigInt) -> Response { if let Ok(conn) = p.get_conn().await { let q = "SELECT secret, name, joindate, status, permissions FROM members WHERE id = :id"; let db_res : Result<(Conn, Self::Row), SqlError> = conn.first_exec(q, params!{"id" => id}).await; if let Ok((_, row)) = db_res { return match row { Some(row) => Response::Row(Self { id: id, secret: row.0, name: row.1, joindate: row.2, status: row.3, permissions: row.4 }), None => Response::Empty } } return Response::Other(sql_err!("Fetch failed")); } return Response::Other(no_conn!("Member::FromDB::get")); } async fn update(p: &Pool, row: Member) -> Response { let q = r#"UPDATE members SET status = :status, permissions = :perms, name = :name WHERE id = :id"#; if let Ok(conn) = p.get_conn().await { let query = conn.drop_exec(q, params!{ "id" => row.id, "status" => row.status, "name" => row.name, "perms" => row.permissions }).await; return match query { Ok(_) => Response::Success, Err(err) => Response::Other(sql_err!(err)) } } return Response::Other(no_conn!("db::Member::update")); } async fn delete(p: &Pool, id: UBigInt) -> Response { if let Ok(conn) = p.get_conn().await { let q = "DELETE from members WHERE id = :id"; let db_result: Result = conn.drop_exec(q, params!{"id" => id}).await; match Member::get(p, id).await { Response::Row(_) => { if let Ok(conn) = db_result { return match conn.prep_exec("", params!{"id" => id}).await { Ok(_) => Response::Success, Err(_) => Response::Other(sql_err!("Member::FromDB::delete")) } } return Response::Success }, Response::Empty => return Response::Empty, _ => return Response::Other(sql_err!("Member::FromDB::delete | another stupid get happened delid this")) } } return Response::Empty; } async fn filter(p: &Pool, status: Integer) -> Response { //! @params status return match (p.get_conn().await, status) { (Ok(conn), STATUS_ONLINE) | (Ok(conn), STATUS_OFFLINE) | (Ok(conn), STATUS_AWAY) | (Ok(conn), STATUS_DO_NOT_DISTURB) => { // TODO: Allow people to query somewhere in the middle of this set // i.e. we should be able to get user 100 -> 150 instead of just the // first 1000 people let q = "SELECT id, name, status, permissions FROM memebrs WHERE status = :stat LIMIT 1000"; // high limit for now if let Ok(query) = conn.prep_exec(q, params!{"stat" => status}).await { let mapping_r = query.map_and_drop(|row| { let (id, name, status, permissions): (UBigInt, VarChar, Integer, UBigInt) = mysql_async::from_row(row); Member { id: id, secret: "".into(), // no show for obv reasons name: name, joindate: 0, // doesn't matter status: status, permissions: permissions } }).await; match mapping_r { Ok((_, members)) => Response::Set(members), Err(_) => Response::Other(sql_err!("db::Members::filter")) } } else { Response::Other(sql_err!("db::Members::filter")) } } _ => Response::Other(sql_err!("err")) } } } impl Member { pub async fn add(p: &Pool, name: &str, secret: &str, perms: u64) -> Result, SqlError> { //! @param {pool} p //! @param {&str} name of new user //! @param {&str} encrypted secret : userland auth module should have a function for this //! @param {u64} permissions mask //! @returns : on_succes => Ok(Response) //! @returns : on_partial_succes => Ok(Response) //! @returns : on_failure => Err(SomeBS) use chrono::Utc; let conn = p.get_conn().await?; //name //perms //secret let now: BigInt = Utc::now().timestamp(); let conn = conn.drop_exec( "INSERT INTO members(secret, name, joindate, status, permissions) VALUES(:secret, :name, :joindate, :status, :permissions)", mysql_async::params!{ "secret" => secret.clone(), "name" => name.clone(), "joindate" => now, "status" => STATUS_ONLINE, "permissions" => perms }).await?; let (_, opt_id): (Conn, Option) = conn.first_exec( "SELECT id FROM members WHERE secret = :secret", params!{ "secret" => secret.clone() }).await?; if let Some(id) = opt_id { return Ok(Response::Row(Self { id: id, secret: secret.into(), name: name.into(), joindate: now, status: STATUS_ONLINE, permissions: perms })) } return Ok(Response::Empty); } pub async fn update_perms(p: &Pool, uid: UBigInt, permissions: UBigInt) -> Result { //! @return on_sucess Ok(NewPermisionsMask) //! let conn = p.get_conn().await?; conn.drop_exec( "UPDATE members SET permissions = :perms WHERE id = :id", params!{ "id" => uid, "perms" => permissions }).await?; Ok(permissions) } }