1use par::{
2 exchange::{Recv, Send},
3 runtimes::tokio::fork,
4 Dual, Session,
5};
6use std::{collections::HashMap, sync::Arc, time::Duration};
7
8struct Amount(i64);
9struct Money(i64);
10struct InsufficientFunds;
11
12enum Operation {
13 CheckBalance(Send<Amount>),
14 Withdraw(Recv<Amount, Send<Result<Money, InsufficientFunds>>>),
15}
16
17struct Account(String);
18struct InvalidAccount;
19
20type ATM = Send<Account, Recv<Result<Send<Operation>, InvalidAccount>>>;
21type Client = Dual<ATM>; fn check_balance(number: String) -> Client {
24 fork(|atm: ATM| async move {
25 let atm = atm.send(Account(number.clone()));
26 let Ok(atm) = atm.recv1().await else {
27 return println!("Invalid account: {}", number);
28 };
29 let Amount(funds) = atm.choose(Operation::CheckBalance).recv1().await;
30 println!("{} has {}", number, funds);
31 })
32}
33
34fn withdraw(number: String, Amount(requested): Amount) -> Client {
35 fork(|atm: ATM| async move {
36 let Ok(atm) = atm.send(Account(number.clone())).recv1().await else {
37 return println!("Invalid account: {}", number);
38 };
39 let response = atm
40 .choose(Operation::Withdraw)
41 .send(Amount(requested))
42 .recv1()
43 .await;
44 match response {
45 Ok(Money(withdrawn)) => println!("{} withdrawn from {}", withdrawn, number),
46 Err(InsufficientFunds) => println!(
47 "{} has insufficient funds to withdraw {}",
48 number, requested
49 ),
50 }
51 })
52}
53
54fn boot_atm(accounts: Arc<HashMap<String, Money>>) -> ATM {
55 fork(|client: Client| async move {
56 let (Account(number), client) = client.recv().await;
57 let Some(&Money(funds)) = accounts.get(&number) else {
58 return client.send1(Err(InvalidAccount));
59 };
60 match client.choose(Ok).recv1().await {
61 Operation::CheckBalance(client) => client.send1(Amount(funds)),
62 Operation::Withdraw(client) => {
63 let (Amount(requested), client) = client.recv().await;
64 if funds >= requested {
65 client.send1(Ok(Money(requested)));
66 } else {
67 client.send1(Err(InsufficientFunds));
68 }
69 }
70 }
71 })
72}
73
74#[tokio::main]
75async fn main() {
76 let accounts = Arc::new(HashMap::from([
77 ("Alice".to_string(), Money(1000)),
78 ("Bob".to_string(), Money(700)),
79 ("Cyril".to_string(), Money(5500)),
80 ]));
81
82 let atm = boot_atm(Arc::clone(&accounts));
83 let client = withdraw("Cyril".to_string(), Amount(2500));
84
85 atm.link(client);
86
87 boot_atm(Arc::clone(&accounts)).link(check_balance("Alice".to_string()));
88 boot_atm(Arc::clone(&accounts)).link(withdraw("Bob".to_string(), Amount(1000)));
89 boot_atm(Arc::clone(&accounts)).link(withdraw("Dylan".to_string(), Amount(20)));
90
91 tokio::time::sleep(Duration::from_secs(1)).await;
92}