use mysql_async::{params, Pool, Conn}; use mysql_async::prelude::Queryable; use mysql_async::error::Error as SqlError; use crate::{Response, no_conn, sql_err}; use crate::{UBigInt, Integer, VarChar}; use crate::{PublicMember, Member}; 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 * */ impl Member { pub async fn get(p: &Pool, id: UBigInt) -> Response { //! @returns Row on success //! @returns Other on failure if let Ok(conn) = p.get_conn().await { let q = "SELECT secret, name, status, permissions FROM members WHERE id = :id"; type Row = Option<(VarChar, VarChar, Integer, UBigInt)>; let db_res : Result<(Conn, 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, secret: row.0, name: row.1, status: row.2, permissions: row.3 }), None => Response::Empty } } return Response::Other(sql_err!("Fetch failed")); } return Response::Other(no_conn!("Member::FromDB::get")); } pub 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 members WHERE status = :stat LIMIT 100"; // 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, secret: "".into(), // no show for obv reasons name, status, permissions } }).await; match mapping_r { Ok((_, members)) => Response::Set(members), Err(_) => Response::Other(sql_err!("db::Members::filter")) } } else { Response::Other(sql_err!("Initial query faile: db::Members::filter")) } } _ => Response::Other(sql_err!("err")) } } 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) let conn = p.get_conn().await?; let conn = conn.drop_exec( "INSERT INTO members(secret, name, status, permissions) VALUES(:secret, :name, :status, :permissions)", mysql_async::params!{ "secret" => secret.clone(), "name" => name.clone(), "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, secret: secret.into(), name: name.into(), 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) //! @throws Err(SqlError) 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) } pub async fn update_nick(p: &Pool, uid: UBigInt, new_nick: &str) -> Result<(), SqlError> { let conn = p.get_conn().await?; conn.drop_exec( "UPDATE members SET name = :nick WHERE id = id", params!{ "id" => uid, "nick" => new_nick }).await?; Ok(()) } } impl PublicMember { pub async fn get_online(p: &Pool, status: Integer) -> Result, SqlError> { let valid_status = status == STATUS_ONLINE || status == STATUS_AWAY || status == STATUS_OFFLINE || status == STATUS_DO_NOT_DISTURB; if !valid_status { return Ok(Response::RestrictedInput(format!("Invalid status value"))); } else { let conn = p.get_conn().await?; let q = "SELECT id, name, permissions FROM members WHERE status = :status LIMIT 1000"; let result = conn.prep_exec(q, params!{"status" => status}).await?; let (_, data): (_, Vec) = result.map_and_drop(|row| { let (id, name, permissions): (UBigInt, VarChar, UBigInt) = mysql_async::from_row(row); PublicMember { id, name, permissions, status } }).await?; return Ok(Response::Set(data)); } } }