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::{UBigInt, BigInt}; use crate::common::FromDB; use crate::{Response, no_conn}; #[allow(dead_code)] pub struct Invite { pub id: BigInt, pub uses: Option, pub expires: bool } #[async_trait] impl FromDB for Invite { type Row = Option<(BigInt, Option, bool)>; async fn get(p: &Pool, id: UBigInt) -> Response { // NOTE: cast is required for this as `id` is used as unix timestamp let id: BigInt = id as BigInt; if id <= 0 { return Response::Empty; } if let Ok(conn) = p.get_conn().await { let q = "SELECT id, uses, expires FROM invites 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(Self { id: id as BigInt, uses: row.1, expires: row.2 }), None => Response::Empty } } } return Response::Empty; } async fn update(p: &Pool, row: Self) -> Response { let q = r#"UPDATE invites SET uses = :uses, expires: :exp WHERE id = :id "#; // forcibly udpate even if theres nothing there // this way we avoid doing an extra network hit if row.id <= 0 { return Response::Empty; } if let Ok(conn) = p.get_conn().await { let result: Result = conn.drop_exec(q, params!{ "id" => row.id, "uses" => row.uses, "exp" => row.expires }).await; return match result { Ok(_) => Response::Success, Err(_) => Response::Other(format!("Could not update entry {}", row.id)) } } return Response::Empty; } async fn delete(p: &Pool, id: UBigInt) -> Response { if id <= 0 { // really lame "assertion" that each method has to use for invites since they all internally use return Response::Empty; } if let Ok(conn) = p.get_conn().await { let q = "DELETE FROM invites WHERE id = :id"; let result: Result = conn.drop_exec(q, params!{"id" => id as BigInt}).await; return match result { Ok(_) => Response::Success, Err(_) => Response::Other(format!("Could not delete {}", id)) } } return Response::Success; } async fn filter(p: &Pool, expirey_flag: bool) -> Response { if let Ok(conn) = p.get_conn().await { let q = "SELECT id, uses, expires FROM invites WHERE expires = :exp"; if let Ok(query) = conn.prep_exec(q, params!{"exp" => expirey_flag}).await { let mapping_r = query.map_and_drop(|row| { let (id, uses): (BigInt, Option) = mysql_async::from_row(row); Invite { id: id, uses: uses, expires: expirey_flag } }).await; return match mapping_r { Ok((_, invites)) => Response::Set(invites), Err(_) => Response::Empty } } else { return Response::Other(no_conn!("db::Invite::filter")); } } else { return Response::Other(no_conn!("db::Invites::filter")); } } } impl Invite { pub fn new(uses: Option, expires: bool) -> Invite { use chrono::Utc; Invite { id: (Utc::now() + chrono::Duration::minutes(30)).timestamp(), uses: uses, expires: expires } } pub async fn add(&self, p: &Pool) -> Result<(), SqlError> { let conn = p.get_conn().await?; conn.prep_exec( "INSERT INTO invites (id, uses, expires) VALUES (:id, :uses, :expires)", params!{ "id" => self.id, "uses" => self.uses, "expires" => self.expires }).await?; Ok(()) } }