use crate::async_handler; use actix::{Handler, ResponseActFuture, WrapFuture}; use sqlx::PgPool; use crate::database::Database; use crate::model::{AccountId, Email, FullAccount, Login, PassHash, Role}; use super::Result; #[derive(Debug, thiserror::Error)] pub enum Error { #[error("Can't create account")] CantCreate, #[error("Can't find account does to lack of identity")] NoIdentity, #[error("Account does not exists")] NotExists, #[error("Failed to load all accounts")] All, } #[derive(actix::Message)] #[rtype(result = "Result>")] pub struct AllAccounts; async_handler!(AllAccounts, all_accounts, Vec); pub async fn all_accounts(_msg: AllAccounts, pool: PgPool) -> Result> { sqlx::query_as( r#" SELECT id, email, login, pass_hash, role FROM accounts "#, ) .fetch_all(&pool) .await .map_err(|e| { log::error!("{e:?}"); super::Error::Account(Error::All) }) } #[derive(actix::Message)] #[rtype(result = "Result")] pub struct CreateAccount { pub email: Email, pub login: Login, pub pass_hash: PassHash, pub role: Role, } async_handler!(CreateAccount, create_account, FullAccount); async fn create_account(msg: CreateAccount, db: PgPool) -> Result { sqlx::query_as( r#" INSERT INTO accounts (login, email, role, pass_hash) VALUES ($1, $2, $3, $4) RETURNING id, email, login, pass_hash, role "#, ) .bind(msg.login) .bind(msg.email) .bind(msg.role) .bind(msg.pass_hash) .fetch_one(&db) .await .map_err(|e| { log::error!("{e:?}"); super::Error::Account(Error::CantCreate) }) } #[derive(actix::Message)] #[rtype(result = "Result")] pub struct FindAccount { pub account_id: AccountId, } async_handler!(FindAccount, find_account, FullAccount); async fn find_account(msg: FindAccount, db: PgPool) -> Result { sqlx::query_as( r#" SELECT id, email, login, pass_hash, role FROM accounts WHERE id = $1 "#, ) .bind(msg.account_id) .fetch_one(&db) .await .map_err(|e| { log::error!("{e:?}"); super::Error::Account(Error::NotExists) }) } #[derive(actix::Message)] #[rtype(result = "Result")] pub struct AccountByIdentity { pub login: Option, pub email: Option, } async_handler!(AccountByIdentity, account_by_identity, FullAccount); async fn account_by_identity(msg: AccountByIdentity, db: PgPool) -> Result { match (msg.login, msg.email) { (Some(login), None) => sqlx::query_as( r#" SELECT id, email, login, pass_hash, role FROM accounts WHERE login = $1 "#, ) .bind(login), (None, Some(email)) => sqlx::query_as( r#" SELECT id, email, login, pass_hash, role FROM accounts WHERE email = $1 "#, ) .bind(email), (Some(login), Some(email)) => sqlx::query_as( r#" SELECT id, email, login, pass_hash, role FROM accounts WHERE login = $1 AND email = $2 "#, ) .bind(login) .bind(email), _ => return Err(super::Error::Account(Error::NoIdentity)), } .fetch_one(&db) .await .map_err(|e| { log::error!("{e:?}"); super::Error::Account(Error::CantCreate) }) }