#include #include #include #include #include #include #define RESET "\033[0m" #define RED "❌ \033[31m" #define GREEN "✅ \033[32m" #define BOLDWHITE "\033[1m\033[37m" using money_t = int64_t; using account_n = std::string; constexpr size_t ACCOUNT_LEN = 4; class Bank; class Account; enum class MenuState { MENU, VIEW = 1, ADD_ACCOUNT = 2, TRANSFER = 3, DEPOSIT = 4, WITHDRAW = 5, EXIT = 6, }; //###################################################### class BankingException : public std::exception { public: const char* what() const noexcept override { return "Internal exception!"; } }; class InsufficientFundsException : public BankingException { const char* what() const noexcept override { return "Insufficient funds!"; } }; class InvalidAmountException : public BankingException { const char* what() const noexcept override { return "Invalid amount!"; } }; class InvalidAccountException : public BankingException { const char* what() const noexcept override { return "Invalid account number!"; } }; class InvalidArgument : public BankingException { const char* what() const noexcept override { return "Invalid argument!"; } }; //###################################################### std::istream& operator>>(std::istream& is, MenuState& state) { int a; if (!(is >> a)) { is.clear(); is.ignore(std::numeric_limits::max(), '\n'); throw InvalidArgument(); } state = static_cast(a); return is; } template T getInput(std::istream& is) { T n; if (!(is >> n) || n < 0) { is.clear(); is.ignore(std::numeric_limits::max(), '\n'); throw InvalidAmountException(); } return n; } template <> account_n getInput(std::istream& is) { std::string s; int64_t n; if (!(is >> s)) { is.clear(); is.ignore(std::numeric_limits::max(), '\n'); throw InvalidAmountException(); } std::istringstream iss(s); if (!(iss >> n) || n < 0 || s.size() != ACCOUNT_LEN) { is.clear(); is.ignore(std::numeric_limits::max(), '\n'); throw InvalidAmountException(); } return s; } constexpr const char* MENU_MSG = "=== Bank Account Management System ===\n1. View balance\n2. Create account\n3. Transfer funds\n4. Deposit funds\n5. Withdraw funds\n6. Exit\n"; //###################################################### class Account{ private: money_t m_balance; public: Account(money_t m = 0) : m_balance(m) {} ~Account() = default; money_t getBalance() { return m_balance; } bool deposit(money_t m) { if (m < 1) throw InvalidAmountException(); if (m + m_balance < m_balance) throw std::overflow_error("Account overflow"); m_balance += m; return true; } bool withdraw(money_t m) { if (m > m_balance) throw InsufficientFundsException(); m_balance -= m; return true; } void setBalance(money_t m) { m_balance = m; } }; //###################################################### class Bank { private: std::map db; public: Bank() = default; ~Bank() = default; account_n addAccount(account_n n = 0, money_t b = 0) { if (n == "0000") n = db.size(); // generate account number auto it = db.emplace(n, Account(b)); if( !it.second ) throw InvalidAccountException(); return it.first->first; } bool transfer(account_n from, account_n to, money_t amount) { auto itFrom = db.find(from); auto itTo = db.find(to); money_t fromState; if (itFrom == db.end() || itTo == db.end() || itFrom == itTo) throw InvalidAccountException(); fromState = itFrom->second.getBalance(); itFrom->second.withdraw(amount); try { itTo->second.deposit(amount); } catch(std::exception& e) { itFrom->second.setBalance(fromState); throw; } return true; } bool deposit(account_n a, money_t m) { auto it = db.find(a); if (it == db.end()) throw InvalidAccountException(); return it->second.deposit(m); } bool withdraw(account_n a, money_t m) { auto it = db.find(a); if (it == db.end()) throw InvalidAccountException(); return it->second.withdraw(m); } money_t getBalance(account_n a) { auto it = db.find(a); if (it == db.end()) throw InvalidAccountException(); return it->second.getBalance(); } }; //###################################################### int main(int ragc, char* argv[]) { std::ostream& os = std::cout; std::istream& is = std::cin; MenuState state = MenuState::MENU; Bank b; for (;;) { try { account_n from = "0000"; account_n to = "0000"; money_t amount = 0; os << BOLDWHITE << MENU_MSG << RESET; is >> state; switch (state) { case MenuState::VIEW : os << "Account number: "; from = getInput(is); amount = b.getBalance(from); os << GREEN << "Balance: " << "$" << amount << "\n" << RESET; break; case MenuState::TRANSFER : os << "From account: "; from = getInput(is); os << "To account: "; to = getInput(is); os << "Amount: $"; amount = getInput(is); b.transfer(from, to, amount); os << GREEN << "$" << amount << " transfered from " << from << " to " << to << "\n" << RESET; break; case MenuState::ADD_ACCOUNT : os << "New account number (0 for auto): "; from = getInput(is); os << "Starting balance: $"; amount = getInput(is); from = b.addAccount(from, amount); os << GREEN << "Account " << from << " created with $" << amount << RESET; break; case MenuState::DEPOSIT : os << "To account: "; to = getInput(is); os << "Amount: $"; amount = getInput(is); b.deposit(to, amount); os << GREEN << "$" << amount << " deposited\n" << RESET; break; case MenuState::WITHDRAW : os << "From account: "; from = getInput(is); os << "Amount: $"; amount = getInput(is); b.withdraw(from, amount); os << GREEN << "$" << amount << " withdrawed\n" << RESET; break; case MenuState::EXIT : os << "Exiting...\n"; return EXIT_SUCCESS; break; case MenuState::MENU : default: throw InvalidArgument(); } } catch (BankingException& e) { os << RED << e.what() << RESET; // return EXIT_FAILURE; } state = MenuState::MENU; os << "\n\n"; } }