use model::{AccountId, AccountState, Email, FullAccount, Login, PassHash, Role}; use sqlx::PgPool; use super::Result; use crate::{db_async_handler, Database}; #[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; db_async_handler!(AllAccounts, all_accounts, Vec); pub(crate) async fn all_accounts(_msg: AllAccounts, pool: PgPool) -> Result> { sqlx::query_as( r#" SELECT id, email, login, pass_hash, role, customer_id, state 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, } db_async_handler!(CreateAccount, create_account, FullAccount); pub(crate) 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, customer_id, state "#, ) .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 UpdateAccount { pub id: AccountId, pub email: Email, pub login: Login, pub pass_hash: Option, pub role: Role, pub state: AccountState, } db_async_handler!(UpdateAccount, update_account, FullAccount); pub(crate) async fn update_account(msg: UpdateAccount, db: PgPool) -> Result { match msg.pass_hash { Some(hash) => sqlx::query_as( r#" UPDATE accounts SET login = $2 AND email = $3 AND role = $4 AND pass_hash = $5 AND state = $6 WHERE id = $1 RETURNING id, email, login, pass_hash, role, customer_id, state "#, ) .bind(msg.login) .bind(msg.email) .bind(msg.role) .bind(hash) .bind(msg.state), None => sqlx::query_as( r#" UPDATE accounts SET login = $2 AND email = $3 AND role = $4 AND pass_hash = $5 WHERE id = $1 RETURNING id, email, login, pass_hash, role, customer_id, state "#, ) .bind(msg.login) .bind(msg.email) .bind(msg.role) .bind(msg.state), } .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, } db_async_handler!(FindAccount, find_account, FullAccount); pub(crate) async fn find_account(msg: FindAccount, db: PgPool) -> Result { sqlx::query_as( r#" SELECT id, email, login, pass_hash, role, customer_id, state 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, } db_async_handler!(AccountByIdentity, account_by_identity, FullAccount); pub(crate) 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, customer_id, state FROM accounts WHERE login = $1 "#, ) .bind(login), (None, Some(email)) => sqlx::query_as( r#" SELECT id, email, login, pass_hash, role, customer_id, state FROM accounts WHERE email = $1 "#, ) .bind(email), (Some(login), Some(email)) => sqlx::query_as( r#" SELECT id, email, login, pass_hash, role, customer_id, state 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) }) }