bank_account/
bank_account.rs1use actor12::prelude::*;
2use actor12::{Multi, MpscChannel, Call, spawn, Link};
3use std::time::Duration;
4use std::future::Future;
5use tokio::time::sleep;
6
7pub struct BankAccount {
9 balance: f64,
10 account_number: String,
11}
12
13#[derive(Debug)]
15pub struct Deposit {
16 pub amount: f64,
17}
18
19#[derive(Debug)]
20pub struct Withdraw {
21 pub amount: f64,
22}
23
24#[derive(Debug)]
25pub struct GetBalance;
26
27#[derive(Debug)]
28pub struct Transfer {
29 pub to_account: Link<BankAccount>,
30 pub amount: f64,
31}
32
33#[derive(Debug, thiserror::Error)]
35pub enum BankError {
36 #[error("Insufficient funds: balance {balance}, requested {requested}")]
37 InsufficientFunds { balance: f64, requested: f64 },
38 #[error("Invalid amount: {amount}")]
39 InvalidAmount { amount: f64 },
40}
41
42impl Actor for BankAccount {
43 type Spec = (String, f64); type Message = Multi<Self>;
45 type Channel = MpscChannel<Self::Message>;
46 type Cancel = ();
47 type State = ();
48
49 fn state(_spec: &Self::Spec) -> Self::State {}
50
51 fn init(ctx: Init<'_, Self>) -> impl Future<Output = Result<Self, Self::Cancel>> + Send + 'static {
52 let (account_number, initial_balance) = ctx.spec;
53 async move {
54 println!("Bank account {} created with initial balance: ${:.2}", account_number, initial_balance);
55 Ok(BankAccount {
56 balance: initial_balance,
57 account_number,
58 })
59 }
60 }
61}
62
63impl Handler<Deposit> for BankAccount {
64 type Reply = Result<f64, anyhow::Error>;
65
66 async fn handle(&mut self, _ctx: Call<'_, Self, Self::Reply>, msg: Deposit) -> Self::Reply {
67 if msg.amount <= 0.0 {
68 return Err(BankError::InvalidAmount { amount: msg.amount }.into());
69 }
70
71 self.balance += msg.amount;
72 println!("Account {}: Deposited ${:.2}, new balance: ${:.2}",
73 self.account_number, msg.amount, self.balance);
74 Ok(self.balance)
75 }
76}
77
78impl Handler<Withdraw> for BankAccount {
79 type Reply = Result<f64, anyhow::Error>;
80
81 async fn handle(&mut self, _ctx: Call<'_, Self, Self::Reply>, msg: Withdraw) -> Self::Reply {
82 if msg.amount <= 0.0 {
83 return Err(BankError::InvalidAmount { amount: msg.amount }.into());
84 }
85
86 if self.balance < msg.amount {
87 return Err(BankError::InsufficientFunds {
88 balance: self.balance,
89 requested: msg.amount
90 }.into());
91 }
92
93 self.balance -= msg.amount;
94 println!("Account {}: Withdrew ${:.2}, new balance: ${:.2}",
95 self.account_number, msg.amount, self.balance);
96 Ok(self.balance)
97 }
98}
99
100impl Handler<GetBalance> for BankAccount {
101 type Reply = Result<f64, anyhow::Error>;
102
103 async fn handle(&mut self, _ctx: Call<'_, Self, Self::Reply>, _msg: GetBalance) -> Self::Reply {
104 println!("Account {}: Balance inquiry: ${:.2}", self.account_number, self.balance);
105 Ok(self.balance)
106 }
107}
108
109impl Handler<Transfer> for BankAccount {
110 type Reply = Result<(), anyhow::Error>;
111
112 async fn handle(&mut self, _ctx: Call<'_, Self, Self::Reply>, msg: Transfer) -> Self::Reply {
113 if msg.amount <= 0.0 {
114 return Err(BankError::InvalidAmount { amount: msg.amount }.into());
115 }
116
117 if self.balance < msg.amount {
118 return Err(BankError::InsufficientFunds {
119 balance: self.balance,
120 requested: msg.amount
121 }.into());
122 }
123
124 self.balance -= msg.amount;
126 println!("Account {}: Transfer out ${:.2}, new balance: ${:.2}",
127 self.account_number, msg.amount, self.balance);
128
129 let _ = msg.to_account.ask_dyn(Deposit { amount: msg.amount }).await?;
131 println!("Transfer of ${:.2} completed from {} to target account",
132 msg.amount, self.account_number);
133
134 Ok(())
135 }
136}
137
138#[tokio::main]
139async fn main() -> anyhow::Result<()> {
140 let alice_account = spawn::<BankAccount>(("ALICE-001".to_string(), 1000.0));
142 let bob_account = spawn::<BankAccount>(("BOB-002".to_string(), 500.0));
143
144 println!("\n=== Initial Operations ===");
146
147 let alice_balance = alice_account.ask_dyn(GetBalance).await?;
149 let bob_balance = bob_account.ask_dyn(GetBalance).await?;
150 println!("Alice: ${:.2}, Bob: ${:.2}", alice_balance, bob_balance);
151
152 let _ = alice_account.ask_dyn(Deposit { amount: 200.0 }).await?;
154
155 let _ = bob_account.ask_dyn(Withdraw { amount: 100.0 }).await?;
157
158 println!("\n=== Transfer Operation ===");
159
160 alice_account.ask_dyn(Transfer {
162 to_account: bob_account.clone(),
163 amount: 300.0
164 }).await?;
165
166 println!("\n=== Final Balances ===");
167 let alice_final = alice_account.ask_dyn(GetBalance).await?;
168 let bob_final = bob_account.ask_dyn(GetBalance).await?;
169 println!("Alice: ${:.2}, Bob: ${:.2}", alice_final, bob_final);
170
171 println!("\n=== Error Handling ===");
172
173 match alice_account.ask_dyn(Withdraw { amount: 2000.0 }).await {
175 Ok(_) => println!("Withdrawal succeeded unexpectedly!"),
176 Err(e) => println!("Withdrawal failed as expected: {}", e),
177 }
178
179 sleep(Duration::from_millis(100)).await;
181
182 Ok(())
183}