1+ // Package challenge7 contains the solution for Challenge 7: Bank Account with Error Handling.
2+ package challenge7
3+
4+ import (
5+ "fmt"
6+ "strings"
7+ "sync"
8+ )
9+
10+ // BankAccount represents a bank account with balance management and minimum balance requirements.
11+ type BankAccount struct {
12+ ID string
13+ Owner string
14+ Balance float64
15+ MinBalance float64
16+ mu sync.Mutex // For thread safety
17+ }
18+
19+ // Constants for account operations
20+ const (
21+ MaxTransactionAmount = 10000.0 // Example limit for deposits/withdrawals
22+ )
23+
24+ // Custom error types
25+
26+ // AccountError is a general error type for bank account operations.
27+ type AccountError struct {
28+ message string
29+ }
30+
31+ func (e * AccountError ) Error () string {
32+ return e .message
33+ }
34+
35+ // InsufficientFundsError occurs when a withdrawal or transfer would bring the balance below minimum.
36+ type InsufficientFundsError struct {
37+ id string
38+ owner string
39+ amtName string
40+ amount float64
41+ min float64
42+ }
43+
44+ func (e * InsufficientFundsError ) Error () string {
45+ return fmt .Sprintf ("Acc Id: %s, owner: %s - %s value $%.2f is below minimum $%.2f" , e .id , e .owner , e .amtName , e .amount , e .min )
46+ }
47+
48+ // NegativeAmountError occurs when an amount for deposit, withdrawal, or transfer is negative.
49+ type NegativeAmountError struct {
50+ id string
51+ owner string
52+ amtName string
53+ amount float64
54+ }
55+
56+ func (e * NegativeAmountError ) Error () string {
57+ return fmt .Sprintf ("Acc Id: %s, owner: %s - %s value $%.2f is negative" , e .id , e .owner , e .amtName , e .amount )
58+ }
59+
60+ // ExceedsLimitError occurs when a deposit or withdrawal amount exceeds the defined limit.
61+ type ExceedsLimitError struct {
62+ id string
63+ owner string
64+ amtName string
65+ amount float64
66+ }
67+
68+ func (e * ExceedsLimitError ) Error () string {
69+ return fmt .Sprintf ("Acc Id: %s, owner: %s - %s value $%.2f exceeds max transaction limit $%.2f" ,
70+ e .id , e .owner , e .amtName , e .amount , MaxTransactionAmount )
71+ }
72+
73+ // NewBankAccount creates a new bank account with the given parameters.
74+ // It returns an error if any of the parameters are invalid.
75+ func NewBankAccount (id , owner string , initialBalance , minBalance float64 ) (* BankAccount , error ) {
76+ if len (strings .TrimSpace (id )) == 0 {
77+ return nil , & AccountError {"invalid id" }
78+ }
79+ if len (strings .TrimSpace (owner )) == 0 {
80+ return nil , & AccountError {"invalid owner" }
81+ }
82+ if initialBalance < 0 {
83+ return nil , & NegativeAmountError {
84+ id : id ,
85+ owner : owner ,
86+ amtName : "new account initial balance" ,
87+ amount : initialBalance ,
88+ }
89+ }
90+ if minBalance < 0 {
91+ return nil , & NegativeAmountError {
92+ id : id ,
93+ owner : owner ,
94+ amtName : "new account minimum balance" ,
95+ amount : minBalance ,
96+ }
97+ } // "initial balance less than minimum"
98+ if initialBalance < minBalance {
99+ return nil , & InsufficientFundsError {
100+ id : id ,
101+ owner : owner ,
102+ amtName : "new account initial balance" ,
103+ amount : initialBalance ,
104+ min : minBalance ,
105+ }
106+ }
107+
108+ acc := BankAccount {
109+ ID : id ,
110+ Owner : owner ,
111+ Balance : initialBalance ,
112+ MinBalance : minBalance ,
113+ }
114+ return & acc , nil
115+ }
116+
117+ // Deposit adds the specified amount to the account balance.
118+ // It returns an error if the amount is invalid or exceeds the transaction limit.
119+ func (a * BankAccount ) Deposit (amount float64 ) error {
120+ // lock mutex
121+ a .mu .Lock ()
122+ defer a .mu .Unlock ()
123+
124+ // validate amount
125+ if amount < 0 {
126+ return & NegativeAmountError {
127+ id : a .ID ,
128+ owner : a .Owner ,
129+ amtName : "deposit amount" ,
130+ amount : amount ,
131+ }
132+ }
133+ if amount > MaxTransactionAmount {
134+ return & ExceedsLimitError {
135+ id : a .ID ,
136+ owner : a .Owner ,
137+ amtName : "deposit amount" ,
138+ amount : amount ,
139+ }
140+ }
141+
142+ // increment balance
143+ a .Balance += amount
144+
145+ return nil
146+ }
147+
148+ // Withdraw removes the specified amount from the account balance.
149+ // It returns an error if the amount is invalid, exceeds the transaction limit,
150+ // or would bring the balance below the minimum required balance.
151+ func (a * BankAccount ) Withdraw (amount float64 ) error {
152+ // lock mutex
153+ a .mu .Lock ()
154+ defer a .mu .Unlock ()
155+
156+ // validate amount
157+ if amount < 0 {
158+ return & NegativeAmountError {
159+ id : a .ID ,
160+ owner : a .Owner ,
161+ amtName : "withdrawal amount" ,
162+ amount : amount ,
163+ }
164+ }
165+ if amount > MaxTransactionAmount {
166+ return & ExceedsLimitError {
167+ id : a .ID ,
168+ owner : a .Owner ,
169+ amtName : "withdrawal amount" ,
170+ amount : amount ,
171+ }
172+ }
173+ if a .Balance - amount < a .MinBalance {
174+ return & InsufficientFundsError {
175+ id : a .ID ,
176+ owner : a .Owner ,
177+ amtName : "remaining balance" ,
178+ amount : a .Balance - amount ,
179+ min : a .MinBalance ,
180+ }
181+ }
182+
183+ // decrement balance
184+ a .Balance -= amount
185+
186+ return nil
187+ }
188+
189+ // Transfer moves the specified amount from this account to the target account.
190+ // It returns an error if the amount is invalid, exceeds the transaction limit,
191+ // or would bring the balance below the minimum required balance.
192+ func (a * BankAccount ) Transfer (amount float64 , target * BankAccount ) error {
193+ // capture initial balances
194+ initSrcBal := a .Balance
195+ initTgtBal := target .Balance
196+
197+ if err := a .Withdraw (amount ); err != nil {
198+ // revert initial balances
199+ a .Balance = initSrcBal
200+ target .Balance = initTgtBal
201+ return err
202+ }
203+ if err := target .Deposit (amount ); err != nil {
204+ // revert initial balances
205+ a .Balance = initSrcBal
206+ target .Balance = initTgtBal
207+ return err
208+ }
209+ return nil
210+ }
0 commit comments